From b9460c849ffa042d59ba23f43432e33affbafc13 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 May 2025 00:19:31 +0000 Subject: [PATCH 1/5] Initial plan for issue From 85f876476449678aa772c5ca4ee7600cf61b2b34 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 May 2025 01:07:04 +0000 Subject: [PATCH 2/5] Optimize array allocations in ValidationEndpointFilterFactory Co-authored-by: captainsafia <1857993+captainsafia@users.noreply.github.com> --- .../src/ValidationEndpointFilterFactory.cs | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/src/Http/Routing/src/ValidationEndpointFilterFactory.cs b/src/Http/Routing/src/ValidationEndpointFilterFactory.cs index 73a41f0f8d57..125c4f4d9692 100644 --- a/src/Http/Routing/src/ValidationEndpointFilterFactory.cs +++ b/src/Http/Routing/src/ValidationEndpointFilterFactory.cs @@ -14,6 +14,21 @@ namespace Microsoft.AspNetCore.Http.Validation; internal static class ValidationEndpointFilterFactory { + // A small struct to hold the validatable parameter details to avoid allocating arrays for parameters that don't need validation + private readonly struct ValidatableParameterEntry + { + public ValidatableParameterEntry(int index, IValidatableInfo parameter, string displayName) + { + Index = index; + Parameter = parameter; + DisplayName = displayName; + } + + public int Index { get; } + public IValidatableInfo Parameter { get; } + public string DisplayName { get; } + } + public static EndpointFilterDelegate Create(EndpointFilterFactoryContext context, EndpointFilterDelegate next) { var parameters = context.MethodInfo.GetParameters(); @@ -25,12 +40,10 @@ public static EndpointFilterDelegate Create(EndpointFilterFactoryContext context var serviceProviderIsService = context.ApplicationServices.GetService(); - var parameterCount = parameters.Length; - var validatableParameters = new IValidatableInfo[parameterCount]; - var parameterDisplayNames = new string[parameterCount]; - var hasValidatableParameters = false; + // Use a list to only store validatable parameters instead of arrays for all parameters + var validatableParameters = new System.Collections.Generic.List(); - for (var i = 0; i < parameterCount; i++) + for (var i = 0; i < parameters.Length; i++) { // Ignore parameters that are resolved from the DI container. if (IsServiceParameter(parameters[i], serviceProviderIsService)) @@ -40,13 +53,14 @@ public static EndpointFilterDelegate Create(EndpointFilterFactoryContext context if (options.TryGetValidatableParameterInfo(parameters[i], out var validatableParameter)) { - validatableParameters[i] = validatableParameter; - parameterDisplayNames[i] = GetDisplayName(parameters[i]); - hasValidatableParameters = true; + validatableParameters.Add(new ValidatableParameterEntry( + i, + validatableParameter, + GetDisplayName(parameters[i]))); } } - if (!hasValidatableParameters) + if (validatableParameters.Count == 0) { return next; } @@ -55,18 +69,20 @@ public static EndpointFilterDelegate Create(EndpointFilterFactoryContext context { ValidateContext? validateContext = null; - for (var i = 0; i < context.Arguments.Count; i++) + foreach (var entry in validatableParameters) { - var validatableParameter = validatableParameters[i]; - var displayName = parameterDisplayNames[i]; + if (entry.Index >= context.Arguments.Count) + { + continue; + } - var argument = context.Arguments[i]; - if (argument is null || validatableParameter is null) + var argument = context.Arguments[entry.Index]; + if (argument is null) { continue; } - var validationContext = new ValidationContext(argument, displayName, context.HttpContext.RequestServices, items: null); + var validationContext = new ValidationContext(argument, entry.DisplayName, context.HttpContext.RequestServices, items: null); if (validateContext == null) { @@ -81,7 +97,7 @@ public static EndpointFilterDelegate Create(EndpointFilterFactoryContext context validateContext.ValidationContext = validationContext; } - await validatableParameter.ValidateAsync(argument, validateContext, context.HttpContext.RequestAborted); + await entry.Parameter.ValidateAsync(argument, validateContext, context.HttpContext.RequestAborted); } if (validateContext is { ValidationErrors.Count: > 0 }) From 8ddca5e3cd1288434a60c36d7b248a47470e8727 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 May 2025 16:20:28 +0000 Subject: [PATCH 3/5] Refine ValidationEndpointFilterFactory to use record struct and improve code style Co-authored-by: captainsafia <1857993+captainsafia@users.noreply.github.com> --- .../src/ValidationEndpointFilterFactory.cs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/Http/Routing/src/ValidationEndpointFilterFactory.cs b/src/Http/Routing/src/ValidationEndpointFilterFactory.cs index 125c4f4d9692..b380b3ebc97b 100644 --- a/src/Http/Routing/src/ValidationEndpointFilterFactory.cs +++ b/src/Http/Routing/src/ValidationEndpointFilterFactory.cs @@ -15,19 +15,7 @@ namespace Microsoft.AspNetCore.Http.Validation; internal static class ValidationEndpointFilterFactory { // A small struct to hold the validatable parameter details to avoid allocating arrays for parameters that don't need validation - private readonly struct ValidatableParameterEntry - { - public ValidatableParameterEntry(int index, IValidatableInfo parameter, string displayName) - { - Index = index; - Parameter = parameter; - DisplayName = displayName; - } - - public int Index { get; } - public IValidatableInfo Parameter { get; } - public string DisplayName { get; } - } + private readonly record struct ValidatableParameterEntry(int Index, IValidatableInfo Parameter, string DisplayName); public static EndpointFilterDelegate Create(EndpointFilterFactoryContext context, EndpointFilterDelegate next) { @@ -41,7 +29,7 @@ public static EndpointFilterDelegate Create(EndpointFilterFactoryContext context var serviceProviderIsService = context.ApplicationServices.GetService(); // Use a list to only store validatable parameters instead of arrays for all parameters - var validatableParameters = new System.Collections.Generic.List(); + var validatableParameters = new List(); for (var i = 0; i < parameters.Length; i++) { @@ -73,7 +61,7 @@ public static EndpointFilterDelegate Create(EndpointFilterFactoryContext context { if (entry.Index >= context.Arguments.Count) { - continue; + break; } var argument = context.Arguments[entry.Index]; From b2a7a8ee31d53d8d9822854530a87b7a42090c1d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 22 May 2025 18:56:51 +0000 Subject: [PATCH 4/5] Avoid List allocation in ValidationEndpointFilterFactory when no validatable parameters Co-authored-by: BrennanConroy <7574801+BrennanConroy@users.noreply.github.com> --- src/Http/Routing/src/ValidationEndpointFilterFactory.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Http/Routing/src/ValidationEndpointFilterFactory.cs b/src/Http/Routing/src/ValidationEndpointFilterFactory.cs index b380b3ebc97b..a7fb2b31bad1 100644 --- a/src/Http/Routing/src/ValidationEndpointFilterFactory.cs +++ b/src/Http/Routing/src/ValidationEndpointFilterFactory.cs @@ -29,7 +29,7 @@ public static EndpointFilterDelegate Create(EndpointFilterFactoryContext context var serviceProviderIsService = context.ApplicationServices.GetService(); // Use a list to only store validatable parameters instead of arrays for all parameters - var validatableParameters = new List(); + List? validatableParameters = null; for (var i = 0; i < parameters.Length; i++) { @@ -41,6 +41,7 @@ public static EndpointFilterDelegate Create(EndpointFilterFactoryContext context if (options.TryGetValidatableParameterInfo(parameters[i], out var validatableParameter)) { + validatableParameters ??= new(); validatableParameters.Add(new ValidatableParameterEntry( i, validatableParameter, @@ -48,7 +49,7 @@ public static EndpointFilterDelegate Create(EndpointFilterFactoryContext context } } - if (validatableParameters.Count == 0) + if (validatableParameters is null || validatableParameters.Count == 0) { return next; } From a600bcd83f1d5b42ea8a36c7b17c78f172ceb6b4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 23 May 2025 18:58:56 +0000 Subject: [PATCH 5/5] Use collection expression syntax for list initialization Co-authored-by: captainsafia <1857993+captainsafia@users.noreply.github.com> --- src/Http/Routing/src/ValidationEndpointFilterFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Routing/src/ValidationEndpointFilterFactory.cs b/src/Http/Routing/src/ValidationEndpointFilterFactory.cs index a7fb2b31bad1..1d8fe04e3dae 100644 --- a/src/Http/Routing/src/ValidationEndpointFilterFactory.cs +++ b/src/Http/Routing/src/ValidationEndpointFilterFactory.cs @@ -41,7 +41,7 @@ public static EndpointFilterDelegate Create(EndpointFilterFactoryContext context if (options.TryGetValidatableParameterInfo(parameters[i], out var validatableParameter)) { - validatableParameters ??= new(); + validatableParameters ??= []; validatableParameters.Add(new ValidatableParameterEntry( i, validatableParameter,