Skip to content

feat: add StringSyntaxAttribute for guid and timespan args #48402

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 57 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
4458e40
feat: add StringSyntaxAttribute for guid args
May 24, 2023
4e17e96
[Blazor] Update template localization file (#48405)
javiercn May 24, 2023
7e3dcf7
Ensure ComponentState is always disposed (#48406)
SteveSandersonMS May 24, 2023
add60ed
Disable debugger support in trimming tests (#48399)
amcasey May 24, 2023
91d702c
Use `Type.IsByRefLike`. (#48351)
teo-tsirpanis May 24, 2023
90599c6
Update JSInProcessObjectReference.cs (#48287)
LostBeard May 24, 2023
a9eb3e8
Transitive package to provide coherency in dotnet/extensions (#48398)
RussKie May 24, 2023
b441d4e
Add test retries for InjectedStartup_DefaultApplicationNameIsEntryAss…
javiercn May 24, 2023
b9da6a0
[main] Update dependencies from dotnet/runtime (#48407)
dotnet-maestro[bot] May 25, 2023
51293e3
Update dependencies from https://github.com/dotnet/source-build-refer…
dotnet-maestro[bot] May 25, 2023
f56c242
[SignalR] Seamless Reconnect (#48338)
BrennanConroy May 26, 2023
f53a14c
Change API template to only use Invariant Globalization in Native AOT…
mitchdenny May 26, 2023
775b001
Update dependencies from https://github.com/dotnet/runtime build 2023…
dotnet-maestro[bot] May 26, 2023
5929e03
[main] (deps): Bump src/submodules/spa-templates (#48445)
dependabot[bot] May 26, 2023
a80aeff
[main] (deps): Bump src/submodules/googletest (#48446)
dependabot[bot] May 26, 2023
7c7c9f0
Remove IdentityServer from spa-templates (#48430)
halter73 May 26, 2023
08a443e
[Templates] Update LoginDisplay.IndividualB2CAuth.razor (#47654)
MSFTJim May 26, 2023
0f8dae4
Update project resolution and app settings writes in `dotnet user-jwt…
captainsafia May 26, 2023
d19463b
Migrate RDF-specific tests to shared infrastructure (part 2) (#48369)
captainsafia May 26, 2023
2a70935
The unit test now uses the correct exception handler (#48459)
bjornen77 May 26, 2023
d0e9442
Update dependencies from https://github.com/dotnet/runtime build 2023…
dotnet-maestro[bot] May 27, 2023
f6048bb
Update dependencies from https://github.com/dotnet/source-build-exter…
dotnet-maestro[bot] May 29, 2023
229a972
Update dependencies from https://github.com/dotnet/efcore build 20230…
dotnet-maestro[bot] May 29, 2023
93bf9f4
[Blazor] Fix redundant use of compounding bitwise |= true (#48466)
blake-fm May 29, 2023
483c972
Localized file check-in by OneLocBuild Task: Build definition ID 1159…
dotnet-bot May 30, 2023
29c5eed
Update dependencies from https://github.com/dotnet/runtime build 2023…
dotnet-maestro[bot] May 30, 2023
9123301
Update SDK (#48252)
halter73 May 30, 2023
dc6a650
feat: add StringSyntaxAttribute for timespan format args
May 30, 2023
81666ee
Fix display format for type. (#48419)
mitchdenny May 30, 2023
4eae56d
[Blazor] Update template to only include inspectUri when using Web as…
javiercn May 30, 2023
c1f7320
Make ProblemDetails settable in ProblemDetailsContext (#48510)
bjornen77 May 31, 2023
690d782
Verify response json body (#48534)
mitchdenny May 31, 2023
2c4a51e
Update dependencies from https://github.com/dotnet/runtime build 2023…
dotnet-maestro[bot] May 31, 2023
61286fd
[Blazor] Initial support for [SupplyParameterFromForm] (#48412)
javiercn May 31, 2023
be14dda
Update method modifiers to address CA1822 (#48551)
mthalman May 31, 2023
6fb6465
[Blazor] Cache LayoutAttribute lookup (#48439)
javiercn Jun 1, 2023
d919168
RDF test migration (part 3) (#48553)
captainsafia Jun 1, 2023
c2488ee
[Blazor] Fix WebView JavaScript boot bug (#48592)
MackinnonBuck Jun 3, 2023
e4c6621
Use FirstPipeInstance setting in named pipes transport (#48609)
JamesNK Jun 6, 2023
738cd32
[main] (deps): Bump src/submodules/spa-templates (#48585)
dependabot[bot] Jun 6, 2023
4e48330
[main] (deps): Bump src/submodules/googletest (#48584)
dependabot[bot] Jun 6, 2023
e5c9648
Expose SNI hostname in ITlsHandshakeFeature for Kestrel/HttpSys (#48572)
karimsalem1 Jun 7, 2023
b97a36e
Minor debugging visualizer cleanup (#48632)
JamesNK Jun 7, 2023
3863fdb
[Blazor] Support for primitive types in [SupplyParameterFromForm] (#4…
javiercn Jun 7, 2023
07e42c1
[Blazor] SupplyParameterFromForm primitive collections (#48559)
javiercn Jun 7, 2023
110522a
[Blazor] SupplyParameterFromForm dictionary support (#48565)
javiercn Jun 7, 2023
ed984e3
Update SDK to 8.0.100-preview.6.23305.3 (#48619)
eerhardt Jun 7, 2023
8dea0b8
[Blazor] SupplyParameterFromForm complex type support. (#48567)
javiercn Jun 7, 2023
97acf9d
Handle methods that never return in Api Analyzers (#48623)
Briaoeuidhtns Jun 7, 2023
0457685
Add missing space to error message (#48653)
danroth27 Jun 7, 2023
ba71be1
ref: add StringSyntaxAttribute to Kestrel server connection guid ids
Jun 7, 2023
aba2c8b
ref: add StringSyntaxAttribute to SignalR Hub connection (guid) ids
Jun 7, 2023
f8807e9
feat: add StringSyntaxAttribute for guid args
May 24, 2023
b72e9b2
feat: add StringSyntaxAttribute for timespan format args
May 30, 2023
f45c90f
ref: add StringSyntaxAttribute to Kestrel server connection guid ids
Jun 7, 2023
cec647b
ref: add StringSyntaxAttribute to SignalR Hub connection (guid) ids
Jun 7, 2023
3b6181d
Merge branch 'main' of https://github.com/abc516/aspnetcore
Jun 7, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions NuGet.config
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<add key="dotnet-eng" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json" />
<add key="dotnet-tools" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json" />
<add key="dotnet8" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8/nuget/v3/index.json" />
<add key="dotnet7" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json" />
<add key="dotnet8-transport" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8-transport/nuget/v3/index.json" />
<add key="dotnet-public" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-public/nuget/v3/index.json" />
<!-- Used for the SiteExtension bits that are included in the 8.0 build -->
Expand Down
2 changes: 2 additions & 0 deletions eng/Dependencies.props
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ and are generated based on the last package release.
<LatestPackageReference Include="Microsoft.Extensions.DependencyInjection" />
<LatestPackageReference Include="Microsoft.Extensions.DependencyModel" />
<LatestPackageReference Include="Microsoft.Extensions.DiagnosticAdapter" />
<LatestPackageReference Include="Microsoft.Extensions.Diagnostics.Abstractions" />
<LatestPackageReference Include="Microsoft.Extensions.Diagnostics" />
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" />
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Composite" />
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Physical" />
Expand Down
2 changes: 2 additions & 0 deletions eng/SharedFramework.External.props
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Diagnostics.Abstractions" Version="$(MicrosoftExtensionsDiagnosticsVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.Diagnostics" Version="$(MicrosoftExtensionsDiagnosticsVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Composite" Version="$(MicrosoftExtensionsFileProvidersCompositeVersion)" />
<ExternalAspNetCoreAppReference Include="Microsoft.Extensions.FileProviders.Physical" Version="$(MicrosoftExtensionsFileProvidersPhysicalVersion)" />
Expand Down
317 changes: 173 additions & 144 deletions eng/Version.Details.xml

Large diffs are not rendered by default.

152 changes: 80 additions & 72 deletions eng/Versions.props

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions eng/test-configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
{"testName": {"contains": "POST_ServerAbort_ClientReceivesAbort"}},
{"testName": {"contains": "POST_ServerAbortAfterWrite_ClientReceivesAbort"}},
{"testName": {"contains": "ServerReset_BeforeRequestBody_ClientBodyThrows"}},
{"testName": {"contains": "InjectedStartup_DefaultApplicationNameIsEntryAssembly"}},
{"testAssembly": {"contains": "IIS"}},
{"testAssembly": {"contains": "Template"}},
{"failureMessage": {"contains":"(Site is started but no worker process found)"}},
Expand Down
2 changes: 1 addition & 1 deletion eng/testing/linker/SupportFiles/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<PublishTrimmed>true</PublishTrimmed>
<TrimMode>full</TrimMode>
<PublishSelfContained>true</PublishSelfContained>
<SelfContained>true</SelfContained>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<!-- Enable NuGet static graph evaluation to optimize incremental restore -->
<RestoreUseStaticGraphEvaluation>true</RestoreUseStaticGraphEvaluation>
Expand Down
2 changes: 1 addition & 1 deletion eng/tools/GenerateFiles/Directory.Build.targets.in
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
BeforeTargets="ProcessFrameworkReferences">
<ItemGroup>
<KnownILLinkPack Include="@(KnownILLinkPack)"
Condition="'%(TargetFramework)' == '${DefaultNetCoreTargetFramework}'"
Condition="'%(TargetFramework)' == 'net7.0'"
TargetFramework="netstandard2.1"
ILLinkPackVersion="%(KnownILLinkPack.ILLinkPackVersion)" />
</ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions global.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{
"sdk": {
"version": "8.0.100-preview.5.23257.1"
"version": "8.0.100-preview.6.23305.3"
},
"tools": {
"dotnet": "8.0.100-preview.5.23257.1",
"dotnet": "8.0.100-preview.6.23305.3",
"runtimes": {
"dotnet/x86": [
"$(MicrosoftNETCoreBrowserDebugHostTransportVersion)"
Expand Down
22 changes: 22 additions & 0 deletions src/Components/Authorization/test/AuthorizeRouteViewTest.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;
using System.Security.Claims;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Components.Binding;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.RenderTree;
using Microsoft.AspNetCore.Components.Test.Helpers;
Expand Down Expand Up @@ -32,6 +34,7 @@ public AuthorizeRouteViewTest()
serviceCollection.AddSingleton<IAuthorizationPolicyProvider, TestAuthorizationPolicyProvider>();
serviceCollection.AddSingleton<IAuthorizationService>(_testAuthorizationService);
serviceCollection.AddSingleton<NavigationManager, TestNavigationManager>();
serviceCollection.AddSingleton<IFormValueSupplier, TestFormValueSupplier>();

var services = serviceCollection.BuildServiceProvider();
_renderer = new TestRenderer(services);
Expand Down Expand Up @@ -467,4 +470,23 @@ public TestNavigationManager()
Initialize("https://localhost:85/subdir/", "https://localhost:85/subdir/path?query=value#hash");
}
}

private class TestFormValueSupplier : IFormValueSupplier
{
public bool CanBind(string formName, Type valueType)
{
return false;
}

public bool CanConvertSingleValue(Type type)
{
return false;
}

public bool TryBind(string formName, Type valueType, [NotNullWhen(true)] out object boundValue)
{
boundValue = null;
return false;
}
}
}
8 changes: 4 additions & 4 deletions src/Components/Components/src/BindConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1223,7 +1223,7 @@ private static bool ConvertToDateTimeCore(object? obj, CultureInfo? culture, out
return ConvertToDateTimeCore(obj, culture, format: null, out value);
}

private static bool ConvertToDateTimeCore(object? obj, CultureInfo? culture, string? format, out DateTime value)
private static bool ConvertToDateTimeCore(object? obj, CultureInfo? culture, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string? format, out DateTime value)
{
var text = (string?)obj;
if (string.IsNullOrEmpty(text))
Expand Down Expand Up @@ -1252,7 +1252,7 @@ private static bool ConvertToNullableDateTimeCore(object? obj, CultureInfo? cult
return ConvertToNullableDateTimeCore(obj, culture, format: null, out value);
}

private static bool ConvertToNullableDateTimeCore(object? obj, CultureInfo? culture, string? format, out DateTime? value)
private static bool ConvertToNullableDateTimeCore(object? obj, CultureInfo? culture, [StringSyntax(StringSyntaxAttribute.DateTimeFormat)] string? format, out DateTime? value)
{
var text = (string?)obj;
if (string.IsNullOrEmpty(text))
Expand Down Expand Up @@ -1336,7 +1336,7 @@ private static bool ConvertToDateTimeOffsetCore(object? obj, CultureInfo? cultur
return ConvertToDateTimeOffsetCore(obj, culture, format: null, out value);
}

private static bool ConvertToDateTimeOffsetCore(object? obj, CultureInfo? culture, string? format, out DateTimeOffset value)
private static bool ConvertToDateTimeOffsetCore(object? obj, CultureInfo? culture, [StringSyntax(StringSyntaxAttribute.DateTimeFormat)] string? format, out DateTimeOffset value)
{
var text = (string?)obj;
if (string.IsNullOrEmpty(text))
Expand Down Expand Up @@ -1365,7 +1365,7 @@ private static bool ConvertToNullableDateTimeOffsetCore(object? obj, CultureInfo
return ConvertToNullableDateTimeOffsetCore(obj, culture, format: null, out value);
}

private static bool ConvertToNullableDateTimeOffsetCore(object? obj, CultureInfo? culture, string? format, out DateTimeOffset? value)
private static bool ConvertToNullableDateTimeOffsetCore(object? obj, CultureInfo? culture, [StringSyntax(StringSyntaxAttribute.DateTimeFormat)] string? format, out DateTimeOffset? value)
{
var text = (string?)obj;
if (string.IsNullOrEmpty(text))
Expand Down
67 changes: 61 additions & 6 deletions src/Components/Components/src/Binding/CascadingModelBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Reflection.Metadata;
using Microsoft.AspNetCore.Components.Binding;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.AspNetCore.Components.Routing;

namespace Microsoft.AspNetCore.Components;

/// <summary>
/// Defines the binding context for data bound from external sources.
/// </summary>
public sealed class CascadingModelBinder : IComponent, IDisposable
public sealed class CascadingModelBinder : IComponent, ICascadingValueComponent, IDisposable
{
private RenderHandle _handle;
private ModelBindingContext? _bindingContext;
private bool _hasPendingQueuedRender;
private BindingInfo? _bindingInfo;

/// <summary>
/// The binding context name.
Expand All @@ -35,7 +38,9 @@ public sealed class CascadingModelBinder : IComponent, IDisposable

[CascadingParameter] ModelBindingContext? ParentContext { get; set; }

[Inject] private NavigationManager Navigation { get; set; } = null!;
[Inject] internal NavigationManager Navigation { get; set; } = null!;

[Inject] internal IFormValueSupplier FormValueSupplier { get; set; } = null!;

void IComponent.Attach(RenderHandle renderHandle)
{
Expand Down Expand Up @@ -87,7 +92,7 @@ private void HandleLocationChanged(object? sender, LocationChangedEventArgs e)
Render();
}

private void UpdateBindingInformation(string url)
internal void UpdateBindingInformation(string url)
{
// BindingContextId: action parameter used to define the handler
// Name: form name and context used to bind
Expand All @@ -103,13 +108,13 @@ private void UpdateBindingInformation(string url)
// 3) Parent has a name "parent-name"
// Name = "parent-name.my-handler";
// BindingContextId = <<base-relative-uri>>((<<existing-query>>&)|?)handler=my-handler
var name = string.IsNullOrEmpty(ParentContext?.Name) ? Name : $"{ParentContext.Name}.{Name}";
var name = ModelBindingContext.Combine(ParentContext, Name);
var bindingId = string.IsNullOrEmpty(name) ? "" : GenerateBindingContextId(name);

var bindingContext = _bindingContext != null &&
string.Equals(_bindingContext.Name, Name, StringComparison.Ordinal) &&
string.Equals(_bindingContext.Name, name, StringComparison.Ordinal) &&
string.Equals(_bindingContext.BindingContextId, bindingId, StringComparison.Ordinal) ?
_bindingContext : new ModelBindingContext(name, bindingId);
_bindingContext : new ModelBindingContext(name, bindingId, FormValueSupplier.CanConvertSingleValue);

// It doesn't matter that we don't check IsFixed, since the CascadingValue we are setting up will throw if the app changes.
if (IsFixed && _bindingContext != null && _bindingContext != bindingContext)
Expand All @@ -136,4 +141,54 @@ void IDisposable.Dispose()
{
Navigation.LocationChanged -= HandleLocationChanged;
}

bool ICascadingValueComponent.CanSupplyValue(Type valueType, string? valueName)
{
var formName = string.IsNullOrEmpty(valueName) ?
(_bindingContext?.Name) :
ModelBindingContext.Combine(_bindingContext, valueName);

if (_bindingInfo != null &&
string.Equals(_bindingInfo.FormName, formName, StringComparison.Ordinal) &&
_bindingInfo.ValueType.Equals(valueType))
{
// We already bound the value, but some component might have been destroyed and
// re-created. If the type and name of the value that we bound are the same,
// we can provide the value that we bound.
return true;
}

// Can't supply the value if this context is for a form with a different name.
if (FormValueSupplier.CanBind(formName!, valueType))
{
var bindingSucceeded = FormValueSupplier.TryBind(formName!, valueType, out var boundValue);
_bindingInfo = new BindingInfo(formName, valueType, bindingSucceeded, boundValue);
if (!bindingSucceeded)
{
// Report errors
}

return true;
}

return false;
}

void ICascadingValueComponent.Subscribe(ComponentState subscriber)
{
throw new InvalidOperationException("Form values are always fixed.");
}

void ICascadingValueComponent.Unsubscribe(ComponentState subscriber)
{
throw new InvalidOperationException("Form values are always fixed.");
}

object? ICascadingValueComponent.CurrentValue => _bindingInfo == null ?
throw new InvalidOperationException("Tried to access form value before it was bound.") :
_bindingInfo.BoundValue;

bool ICascadingValueComponent.CurrentValueIsFixed => true;

private record BindingInfo(string? FormName, Type ValueType, bool BindingResult, object? BoundValue);
}
37 changes: 37 additions & 0 deletions src/Components/Components/src/Binding/IFormValueSupplier.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;

namespace Microsoft.AspNetCore.Components.Binding;

/// <summary>
/// Binds form data valuesto a model.
/// </summary>
public interface IFormValueSupplier
{
/// <summary>
/// Determines whether the specified value type can be bound.
/// </summary>
/// <param name="formName">The form name to bind data from.</param>
/// <param name="valueType">The <see cref="Type"/> for the value to bind.</param>
/// <returns><c>true</c> if the value type can be bound; otherwise, <c>false</c>.</returns>
bool CanBind(string formName, Type valueType);

/// <summary>
/// Determines whether a given <see cref="Type"/> can be converted from a single string value.
/// For example, strings, numbers, boolean values, enums, guids, etc. fall in this category.
/// </summary>
/// <param name="type">The <see cref="Type"/> to check.</param>
/// <returns><c>true</c> if the type can be converted from a single string value; otherwise, <c>false</c>.</returns>
bool CanConvertSingleValue(Type type);

/// <summary>
/// Tries to bind the form with the specified name to a value of the specified type.
/// </summary>
/// <param name="formName">The form name to bind data from.</param>
/// <param name="valueType">The <see cref="Type"/> for the value to bind.</param>
/// <param name="boundValue">The bound value if succeeded.</param>
/// <returns><c>true</c> if the form was bound successfully; otherwise, <c>false</c>.</returns>
bool TryBind(string formName, Type valueType, [NotNullWhen(true)] out object? boundValue);
}
14 changes: 13 additions & 1 deletion src/Components/Components/src/Binding/ModelBindingContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ namespace Microsoft.AspNetCore.Components;
/// </summary>
public sealed class ModelBindingContext
{
internal ModelBindingContext(string name, string bindingContextId)
private readonly Predicate<Type> _canBind;

internal ModelBindingContext(string name, string bindingContextId, Predicate<Type> canBind)
{
ArgumentNullException.ThrowIfNull(name);
ArgumentNullException.ThrowIfNull(bindingContextId);
ArgumentNullException.ThrowIfNull(canBind);
// We are initializing the root context, that can be a "named" root context, or the default context.
// A named root context only provides a name, and that acts as the BindingId
// A "default" root context does not provide a name, and instead it provides an explicit Binding ID.
Expand All @@ -23,6 +26,7 @@ internal ModelBindingContext(string name, string bindingContextId)

Name = name;
BindingContextId = bindingContextId ?? name;
_canBind = canBind;
}

/// <summary>
Expand All @@ -34,4 +38,12 @@ internal ModelBindingContext(string name, string bindingContextId)
/// The computed identifier used to determine what parts of the app can bind data.
/// </summary>
public string BindingContextId { get; }

internal static string Combine(ModelBindingContext? parentContext, string name) =>
string.IsNullOrEmpty(parentContext?.Name) ? name : $"{parentContext.Name}.{name}";

internal bool CanConvert(Type type)
{
return _canBind(type);
}
}
25 changes: 16 additions & 9 deletions src/Components/Components/src/CascadingParameterState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.Collections.Concurrent;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Components.Reflection;
using Microsoft.AspNetCore.Components.Rendering;
Expand Down Expand Up @@ -45,11 +46,8 @@ public static IReadOnlyList<CascadingParameterState> FindCascadingParameters(Com
var supplier = GetMatchingCascadingValueSupplier(info, componentState);
if (supplier != null)
{
if (resultStates == null)
{
// Although not all parameters might be matched, we know the maximum number
resultStates = new List<CascadingParameterState>(infos.Length - infoIndex);
}
// Although not all parameters might be matched, we know the maximum number
resultStates ??= new List<CascadingParameterState>(infos.Length - infoIndex);

resultStates.Add(new CascadingParameterState(info.ConsumerValueName, supplier));
}
Expand Down Expand Up @@ -98,16 +96,25 @@ private static ReflectedCascadingParameterInfo[] CreateReflectedCascadingParamet
var attribute = prop.GetCustomAttribute<CascadingParameterAttribute>();
if (attribute != null)
{
if (result == null)
{
result = new List<ReflectedCascadingParameterInfo>();
}
result ??= new List<ReflectedCascadingParameterInfo>();

result.Add(new ReflectedCascadingParameterInfo(
prop.Name,
prop.PropertyType,
attribute.Name));
}

var hostParameterAttribute = prop.GetCustomAttributes()
.OfType<IHostEnvironmentCascadingParameter>().SingleOrDefault();
if (hostParameterAttribute != null)
{
result ??= new List<ReflectedCascadingParameterInfo>();

result.Add(new ReflectedCascadingParameterInfo(
prop.Name,
prop.PropertyType,
hostParameterAttribute.Name));
}
}

return result?.ToArray() ?? Array.Empty<ReflectedCascadingParameterInfo>();
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Components/src/ComponentBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ Task IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, object? arg)
Task IHandleAfterRender.OnAfterRenderAsync()
{
var firstRender = !_hasCalledOnAfterRender;
_hasCalledOnAfterRender |= true;
_hasCalledOnAfterRender = true;

OnAfterRender(firstRender);

Expand Down
Loading