Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 48 additions & 3 deletions src/Http/Http.Extensions/src/RequestDelegateFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Internal;
Expand Down Expand Up @@ -503,9 +505,9 @@ private static Expression BindParameterFromValue(ParameterInfo parameter, Expres
var isNotNullable = underlyingNullableType is null;

var nonNullableParameterType = underlyingNullableType ?? parameter.ParameterType;
var tryParseMethod = TryParseMethodCache.FindTryParseMethod(nonNullableParameterType);
var tryParseMethodCall = TryParseMethodCache.FindTryParseMethodCall(parameter);

if (tryParseMethod is null)
if (tryParseMethodCall is null)
{
throw new InvalidOperationException($"No public static bool {parameter.ParameterType.Name}.TryParse(string, out {parameter.ParameterType.Name}) method found for {parameter.Name}.");
}
Expand Down Expand Up @@ -560,7 +562,7 @@ private static Expression BindParameterFromValue(ParameterInfo parameter, Expres
Expression.Call(LogParameterBindingFailureMethod,
HttpContextExpr, parameterTypeNameConstant, parameterNameConstant, TempSourceStringExpr));

var tryParseCall = Expression.Call(tryParseMethod, TempSourceStringExpr, parsedValue);
MethodCallExpression? tryParseCall = tryParseMethodCall(TempSourceStringExpr, parsedValue);

// If the parameter is nullable, we need to assign the "parsedValue" local to the nullable parameter on success.
Expression tryParseExpression = isNotNullable ?
Expand All @@ -587,6 +589,49 @@ private static Expression BindParameterFromValue(ParameterInfo parameter, Expres
return argument;
}

private static MethodCallExpression CreateTryParseCall(MethodInfo tryParseMethod, ParameterInfo parameter, Expression parsedValue)
{
// Before call the TryParse Method, we should know the exact param to inject
Type type = Nullable.GetUnderlyingType(parameter.ParameterType) ?? parameter.ParameterType;
var useNumberStyles = TryParseMethodCache.UseTryParseWithNumberStyleOption(type);
var useDateTimeStyles = TryParseMethodCache.UseTryParseWithDateTimeStyleOptions(type);

// Should we use CultureInvariant
if (tryParseMethod.GetParameters().Length > 2)
{
if (useNumberStyles)
{
return Expression.Call(
tryParseMethod,
TempSourceStringExpr,
Expression.Constant(TryParseMethodCache.SetRightNumberStyles(type)),
Expression.Constant(CultureInfo.InvariantCulture),
parsedValue);
}
else if (useDateTimeStyles)
{
return Expression.Call(
tryParseMethod,
TempSourceStringExpr,
Expression.Constant(CultureInfo.InvariantCulture),
Expression.Constant(DateTimeStyles.None),
parsedValue);
}
else
{
return Expression.Call(
tryParseMethod,
TempSourceStringExpr,
Expression.Constant(CultureInfo.InvariantCulture),
parsedValue);
}
}
else
{
return Expression.Call(tryParseMethod, TempSourceStringExpr, parsedValue);
}
}

private static Expression BindParameterFromProperty(ParameterInfo parameter, MemberExpression property, string key, FactoryContext factoryContext) =>
BindParameterFromValue(parameter, GetValueFromProperty(property, key), factoryContext);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;

using Xunit;

namespace Microsoft.AspNetCore.Routing.Internal
Expand Down
67 changes: 67 additions & 0 deletions src/Http/Http.Extensions/test/TryParseMethodCacheTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System;
using System.Globalization;

using Xunit;

namespace Microsoft.AspNetCore.Http.Extensions.Tests
{
public class TryParseMethodCacheTests
{
[Theory]
[InlineData(typeof(int))]
[InlineData(typeof(double))]
[InlineData(typeof(float))]
[InlineData(typeof(Half))]
[InlineData(typeof(short))]
[InlineData(typeof(long))]
[InlineData(typeof(IntPtr))]
[InlineData(typeof(sbyte))]
[InlineData(typeof(ushort))]
[InlineData(typeof(uint))]
[InlineData(typeof(ulong))]
public void FindTryParseMethod_ReturnsTheExpectedTryParseMethodWithInvariantCulture(Type @type)
{
var methodFound = TryParseMethodCache.FindTryParseMethod(@type);

Assert.NotNull(methodFound);

var parameters = methodFound!.GetParameters();
Assert.Equal(4, parameters.Length);
Assert.Equal(typeof(string), parameters[0].ParameterType);
Assert.Equal(typeof(NumberStyles), parameters[1].ParameterType);
Assert.Equal(typeof(IFormatProvider), parameters[2].ParameterType);
Assert.True(parameters[3].IsOut);
}

[Theory]
[InlineData(typeof(DateTime))]
[InlineData(typeof(DateOnly))]
[InlineData(typeof(DateTimeOffset))]
[InlineData(typeof(TimeOnly))]
[InlineData(typeof(TimeSpan))]
public void FindTryParseMethod_ReturnsTheExpectedTryParseMethodWithInvariantCultureDateType(Type @type)
{
var methodFound = TryParseMethodCache.FindTryParseMethod(@type);

Assert.NotNull(methodFound);

var parameters = methodFound!.GetParameters();

if (@type == typeof(TimeSpan))
{
Assert.Equal(3, parameters.Length);
Assert.Equal(typeof(string), parameters[0].ParameterType);
Assert.Equal(typeof(IFormatProvider), parameters[1].ParameterType);
Assert.True(parameters[2].IsOut);
}
else
{
Assert.Equal(4, parameters.Length);
Assert.Equal(typeof(string), parameters[0].ParameterType);
Assert.Equal(typeof(IFormatProvider), parameters[1].ParameterType);
Assert.Equal(typeof(DateTimeStyles), parameters[2].ParameterType);
Assert.True(parameters[3].IsOut);
}
}
}
}
Loading