* Cache ExecutorFactory as part of CompiledActionDescriptor
* Add tests for ExecutorFactory
This commit is contained in:
parent
498c1c570d
commit
de30c5822a
|
|
@ -170,38 +170,6 @@ namespace Microsoft.AspNetCore.Mvc.Abstractions
|
|||
return GetString("BindingSource_Services");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Special
|
||||
/// </summary>
|
||||
internal static string BindingSource_Special
|
||||
{
|
||||
get { return GetString("BindingSource_Special"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Special
|
||||
/// </summary>
|
||||
internal static string FormatBindingSource_Special()
|
||||
{
|
||||
return GetString("BindingSource_Special");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FormFile
|
||||
/// </summary>
|
||||
internal static string BindingSource_FormFile
|
||||
{
|
||||
get { return GetString("BindingSource_FormFile"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FormFile
|
||||
/// </summary>
|
||||
internal static string FormatBindingSource_FormFile()
|
||||
{
|
||||
return GetString("BindingSource_FormFile");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ModelBinding
|
||||
/// </summary>
|
||||
|
|
@ -314,6 +282,38 @@ namespace Microsoft.AspNetCore.Mvc.Abstractions
|
|||
return string.Format(CultureInfo.CurrentCulture, GetString("BindingSource_MustBeGreedy"), p0, p1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Special
|
||||
/// </summary>
|
||||
internal static string BindingSource_Special
|
||||
{
|
||||
get { return GetString("BindingSource_Special"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Special
|
||||
/// </summary>
|
||||
internal static string FormatBindingSource_Special()
|
||||
{
|
||||
return GetString("BindingSource_Special");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FormFile
|
||||
/// </summary>
|
||||
internal static string BindingSource_FormFile
|
||||
{
|
||||
get { return GetString("BindingSource_FormFile"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// FormFile
|
||||
/// </summary>
|
||||
internal static string FormatBindingSource_FormFile()
|
||||
{
|
||||
return GetString("BindingSource_FormFile");
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
// 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.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||
{
|
||||
public class HandlerMethodDescriptor
|
||||
{
|
||||
public MethodInfo Method { get; set; }
|
||||
|
||||
public Func<Page, object, Task<IActionResult>> Executor { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -5,29 +5,31 @@ using System;
|
|||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
public static class ExecutorFactory
|
||||
{
|
||||
public static Func<Page, object, Task<IActionResult>> Create(MethodInfo method)
|
||||
public static Func<Page, object, Task<IActionResult>> CreateExecutor(
|
||||
CompiledPageActionDescriptor actionDescriptor,
|
||||
MethodInfo method)
|
||||
{
|
||||
return new Executor()
|
||||
if (actionDescriptor == null)
|
||||
{
|
||||
Method = method,
|
||||
}.Execute;
|
||||
}
|
||||
throw new ArgumentNullException(nameof(actionDescriptor));
|
||||
}
|
||||
|
||||
private class Executor
|
||||
{
|
||||
public MethodInfo Method { get; set; }
|
||||
|
||||
public async Task<IActionResult> Execute(Page page, object model)
|
||||
if (method == null)
|
||||
{
|
||||
var handler = HandlerMethod.Create(Method);
|
||||
throw new ArgumentNullException(nameof(method));
|
||||
}
|
||||
|
||||
var receiver = Method.DeclaringType.IsAssignableFrom(page.GetType()) ? page : model;
|
||||
var methodIsDeclaredOnPage = method.DeclaringType.GetTypeInfo().IsAssignableFrom(actionDescriptor.PageTypeInfo);
|
||||
var handler = CreateHandlerMethod(method);
|
||||
|
||||
return async (page, model) =>
|
||||
{
|
||||
var arguments = new object[handler.Parameters.Length];
|
||||
for (var i = 0; i < handler.Parameters.Length; i++)
|
||||
{
|
||||
|
|
@ -39,68 +41,69 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
parameter.Name);
|
||||
}
|
||||
|
||||
var receiver = methodIsDeclaredOnPage ? page : model;
|
||||
var result = await handler.Execute(receiver, arguments);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private class HandlerParameter
|
||||
private static HandlerMethod CreateHandlerMethod(MethodInfo method)
|
||||
{
|
||||
public string Name { get; set; }
|
||||
var methodParameters = method.GetParameters();
|
||||
var parameters = new HandlerParameter[methodParameters.Length];
|
||||
|
||||
public Type Type { get; set; }
|
||||
for (var i = 0; i < methodParameters.Length; i++)
|
||||
{
|
||||
var methodParameter = methodParameters[i];
|
||||
object defaultValue = null;
|
||||
if (methodParameter.HasDefaultValue)
|
||||
{
|
||||
defaultValue = methodParameter.DefaultValue;
|
||||
}
|
||||
else if (methodParameter.ParameterType.GetTypeInfo().IsValueType)
|
||||
{
|
||||
defaultValue = Activator.CreateInstance(methodParameter.ParameterType);
|
||||
}
|
||||
|
||||
public object DefaultValue { get; set; }
|
||||
parameters[i] = new HandlerParameter(methodParameter.Name, methodParameter.ParameterType, defaultValue);
|
||||
}
|
||||
|
||||
var returnType = method.ReturnType;
|
||||
var returnTypeInfo = method.ReturnType.GetTypeInfo();
|
||||
if (returnType == typeof(void))
|
||||
{
|
||||
return new VoidHandlerMethod(parameters, method);
|
||||
}
|
||||
else if (typeof(IActionResult).IsAssignableFrom(returnType))
|
||||
{
|
||||
return new ActionResultHandlerMethod(parameters, method);
|
||||
}
|
||||
else if (returnType == typeof(Task))
|
||||
{
|
||||
return new NonGenericTaskHandlerMethod(parameters, method);
|
||||
}
|
||||
else
|
||||
{
|
||||
var taskType = ClosedGenericMatcher.ExtractGenericInterface(returnType, typeof(Task<>));
|
||||
if (taskType != null && typeof(IActionResult).IsAssignableFrom(taskType.GenericTypeArguments[0]))
|
||||
{
|
||||
return new GenericTaskHandlerMethod(parameters, method);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidOperationException(Resources.FormatUnsupportedHandlerMethodType(returnType));
|
||||
}
|
||||
|
||||
private abstract class HandlerMethod
|
||||
{
|
||||
public static HandlerMethod Create(MethodInfo method)
|
||||
{
|
||||
var methodParameters = method.GetParameters();
|
||||
var parameters = new HandlerParameter[methodParameters.Length];
|
||||
|
||||
for (var i = 0; i < methodParameters.Length; i++)
|
||||
{
|
||||
parameters[i] = new HandlerParameter()
|
||||
{
|
||||
DefaultValue = methodParameters[i].HasDefaultValue ? methodParameters[i].DefaultValue : null,
|
||||
Name = methodParameters[i].Name,
|
||||
Type = methodParameters[i].ParameterType,
|
||||
};
|
||||
}
|
||||
|
||||
if (method.ReturnType == typeof(Task))
|
||||
{
|
||||
return new NonGenericTaskHandlerMethod(parameters, method);
|
||||
}
|
||||
else if (method.ReturnType == typeof(void))
|
||||
{
|
||||
return new VoidHandlerMethod(parameters, method);
|
||||
}
|
||||
else if (
|
||||
method.ReturnType.IsConstructedGenericType &&
|
||||
method.ReturnType.GetTypeInfo().GetGenericTypeDefinition() == typeof(Task<>) &&
|
||||
typeof(IActionResult).IsAssignableFrom(method.ReturnType.GetTypeInfo().GetGenericArguments()[0]))
|
||||
{
|
||||
return new GenericTaskHandlerMethod(parameters, method);
|
||||
}
|
||||
else if (typeof(IActionResult).IsAssignableFrom(method.ReturnType))
|
||||
{
|
||||
return new ActionResultHandlerMethod(parameters, method);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("unsupported handler method return type");
|
||||
}
|
||||
}
|
||||
|
||||
protected static Expression[] Unpack(Expression arguments, HandlerParameter[] parameters)
|
||||
{
|
||||
var unpackExpressions = new Expression[parameters.Length];
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
unpackExpressions[i] = Expression.Convert(Expression.ArrayIndex(arguments, Expression.Constant(i)), parameters[i].Type);
|
||||
unpackExpressions[i] = Expression.Convert(
|
||||
Expression.ArrayIndex(arguments, Expression.Constant(i)),
|
||||
parameters[i].Type);
|
||||
}
|
||||
|
||||
return unpackExpressions;
|
||||
|
|
@ -178,7 +181,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
private static async Task<object> Convert<T>(object taskAsObject)
|
||||
{
|
||||
var task = (Task<T>)taskAsObject;
|
||||
return (object)await task;
|
||||
return await task;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -234,5 +237,21 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
return Task.FromResult(_thunk(receiver, arguments));
|
||||
}
|
||||
}
|
||||
|
||||
private struct HandlerParameter
|
||||
{
|
||||
public HandlerParameter(string name, Type type, object defaultValue)
|
||||
{
|
||||
Name = name;
|
||||
Type = type;
|
||||
DefaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public Type Type { get; }
|
||||
|
||||
public object DefaultValue { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -365,7 +365,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var handler = _selector.Select(_pageContext);
|
||||
if (handler != null)
|
||||
{
|
||||
var executor = ExecutorFactory.Create(handler.Method);
|
||||
var executor = handler.Executor;
|
||||
result = await executor(_page, _model);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -239,6 +239,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
actionDescriptor.HandlerMethods.Add(new HandlerMethodDescriptor()
|
||||
{
|
||||
Method = method,
|
||||
Executor = ExecutorFactory.CreateExecutor(actionDescriptor, method),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,6 +90,22 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
return GetString("ArgumentCannotBeNullOrEmpty");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsupported handler method type '{0}'.
|
||||
/// </summary>
|
||||
internal static string UnsupportedHandlerMethodType
|
||||
{
|
||||
get { return GetString("UnsupportedHandlerMethodType"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsupported handler method type '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatUnsupportedHandlerMethodType(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("UnsupportedHandlerMethodType"), p0);
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -132,4 +132,7 @@
|
|||
<data name="ArgumentCannotBeNullOrEmpty" xml:space="preserve">
|
||||
<value>Value cannot be null or empty.</value>
|
||||
</data>
|
||||
<data name="UnsupportedHandlerMethodType" xml:space="preserve">
|
||||
<value>Unsupported handler method return type '{0}'.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -33,6 +33,10 @@
|
|||
"version": "1.2.0-*",
|
||||
"type": "build"
|
||||
},
|
||||
"Microsoft.Extensions.ClosedGenericMatcher.Sources": {
|
||||
"version": "1.2.0-*",
|
||||
"type": "build"
|
||||
},
|
||||
"NETStandard.Library": "1.6.1"
|
||||
},
|
||||
"frameworks": {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,454 @@
|
|||
// 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.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ViewComponents;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
|
||||
{
|
||||
public class ExecutorFactoryTest
|
||||
{
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForActionResultMethod_OnPage()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ActionResultReturningHandler));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new TestPage(), null);
|
||||
var actionResult = await actionResultTask;
|
||||
Assert.IsType<EmptyResult>(actionResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForMethodReturningConcreteSubtypeOfIActionResult_OnPage()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ConcreteActionResult));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new TestPage(), null);
|
||||
var actionResult = await actionResultTask;
|
||||
Assert.IsType<ViewResult>(actionResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForActionResultReturningMethod_WithParameters_OnPage()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ActionResultReturnHandlerWithParameters));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new TestPage(), null);
|
||||
var actionResult = await actionResultTask;
|
||||
var contentResult = Assert.IsType<ContentResult>(actionResult);
|
||||
Assert.Equal("Hello 0", contentResult.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForVoidReturningMethod_OnPage()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
};
|
||||
var page = new TestPage();
|
||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.VoidReturningHandler));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(page, null);
|
||||
var actionResult = await actionResultTask;
|
||||
Assert.Null(actionResult);
|
||||
Assert.True(page.SideEffects);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForVoidTaskReturningMethod_OnPage()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
};
|
||||
var page = new TestPage();
|
||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.VoidTaskReturningHandler));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(page, null);
|
||||
var actionResult = await actionResultTask;
|
||||
Assert.Null(actionResult);
|
||||
Assert.True(page.SideEffects);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForTaskOfIActionResultReturningMethod_OnPage()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.GenericTaskHandler));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new TestPage(), null);
|
||||
var actionResult = await actionResultTask;
|
||||
Assert.IsType<EmptyResult>(actionResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForTaskOfConcreteActionResultReturningMethod_OnPage()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.TaskReturningConcreteSubtype));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new TestPage(), null);
|
||||
var actionResult = await actionResultTask;
|
||||
var contentResult = Assert.IsType<ContentResult>(actionResult);
|
||||
Assert.Equal("value", contentResult.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForActionResultMethod_OnPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.ActionResultReturningHandler));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new EmptyPage(), new TestPageModel());
|
||||
var actionResult = await actionResultTask;
|
||||
Assert.IsType<EmptyResult>(actionResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForMethodReturningConcreteSubtypeOfIActionResult_OnPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.ConcreteActionResult));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new EmptyPage(), new TestPageModel());
|
||||
var actionResult = await actionResultTask;
|
||||
Assert.IsType<ViewResult>(actionResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForActionResultReturningMethod_WithParameters_OnPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.ActionResultReturnHandlerWithParameters));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new EmptyPage(), new TestPageModel());
|
||||
var actionResult = await actionResultTask;
|
||||
var contentResult = Assert.IsType<ContentResult>(actionResult);
|
||||
Assert.Equal("Hello 0", contentResult.Content);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForVoidReturningMethod_OnPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var model = new TestPageModel();
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.VoidReturningHandler));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new EmptyPage(), model);
|
||||
var actionResult = await actionResultTask;
|
||||
Assert.Null(actionResult);
|
||||
Assert.True(model.SideEffects);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForVoidTaskReturningMethod_OnPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var model = new TestPageModel();
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.VoidTaskReturningHandler));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new EmptyPage(), model);
|
||||
var actionResult = await actionResultTask;
|
||||
Assert.Null(actionResult);
|
||||
Assert.True(model.SideEffects);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForTaskOfIActionResultReturningMethod_OnPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.GenericTaskHandler));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new EmptyPage(), new TestPageModel());
|
||||
var actionResult = await actionResultTask;
|
||||
Assert.IsType<EmptyResult>(actionResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateExecutor_ForTaskOfConcreteActionResultReturningMethod_OnPageModel()
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.TaskReturningConcreteSubtype));
|
||||
|
||||
// Act
|
||||
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(executor);
|
||||
var actionResultTask = executor(new EmptyPage(), new TestPageModel());
|
||||
var actionResult = await actionResultTask;
|
||||
var contentResult = Assert.IsType<ContentResult>(actionResult);
|
||||
Assert.Equal("value", contentResult.Content);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(nameof(TestPageModel.StringResult))]
|
||||
[InlineData(nameof(TestPageModel.TaskOfObject))]
|
||||
[InlineData(nameof(TestPageModel.ViewComponent))]
|
||||
public void CreateExecutor_ThrowsIfTypeIsNotAValidReturnType(string methodName)
|
||||
{
|
||||
// Arrange
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(PageModel).GetTypeInfo(),
|
||||
};
|
||||
var methodInfo = typeof(TestPageModel).GetMethod(methodName);
|
||||
|
||||
// Act & Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo));
|
||||
Assert.Equal($"Unsupported handler method return type '{methodInfo.ReturnType}'.", ex.Message);
|
||||
}
|
||||
|
||||
private class TestPage : Page
|
||||
{
|
||||
public TestPage()
|
||||
{
|
||||
Binder = new MockBinder();
|
||||
}
|
||||
|
||||
public bool SideEffects { get; private set; }
|
||||
|
||||
public IActionResult ActionResultReturningHandler() => new EmptyResult();
|
||||
|
||||
public IActionResult ActionResultReturnHandlerWithParameters(int arg1, string arg2 = "Hello")
|
||||
{
|
||||
return new ContentResult
|
||||
{
|
||||
Content = $"{arg2} {arg1}",
|
||||
};
|
||||
}
|
||||
|
||||
public ViewResult ConcreteActionResult() => new ViewResult();
|
||||
|
||||
public void VoidReturningHandler()
|
||||
{
|
||||
SideEffects = true;
|
||||
}
|
||||
|
||||
public async Task VoidTaskReturningHandler()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
SideEffects = true;
|
||||
});
|
||||
}
|
||||
|
||||
public Task<IActionResult> GenericTaskHandler() => Task.FromResult<IActionResult>(new EmptyResult());
|
||||
|
||||
public Task<ContentResult> TaskReturningConcreteSubtype(string arg = "value")
|
||||
{
|
||||
return Task.FromResult(new ContentResult
|
||||
{
|
||||
Content = arg,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public override Task ExecuteAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class TestPageModel
|
||||
{
|
||||
public bool SideEffects { get; private set; }
|
||||
|
||||
public IActionResult ActionResultReturningHandler() => new EmptyResult();
|
||||
|
||||
public IActionResult ActionResultReturnHandlerWithParameters(int arg1, string arg2 = "Hello")
|
||||
{
|
||||
return new ContentResult
|
||||
{
|
||||
Content = $"{arg2} {arg1}",
|
||||
};
|
||||
}
|
||||
|
||||
public ViewResult ConcreteActionResult() => new ViewResult();
|
||||
|
||||
public void VoidReturningHandler()
|
||||
{
|
||||
SideEffects = true;
|
||||
}
|
||||
|
||||
public async Task VoidTaskReturningHandler()
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
SideEffects = true;
|
||||
});
|
||||
}
|
||||
|
||||
public Task<IActionResult> GenericTaskHandler() => Task.FromResult<IActionResult>(new EmptyResult());
|
||||
|
||||
public Task<ContentResult> TaskReturningConcreteSubtype(string arg = "value")
|
||||
{
|
||||
return Task.FromResult(new ContentResult
|
||||
{
|
||||
Content = arg,
|
||||
});
|
||||
}
|
||||
|
||||
public string StringResult() => "";
|
||||
|
||||
public Task<object> TaskOfObject() => Task.FromResult(new object());
|
||||
|
||||
public IViewComponentResult ViewComponent() => new ViewViewComponentResult();
|
||||
}
|
||||
|
||||
private class EmptyPage : Page
|
||||
{
|
||||
public EmptyPage()
|
||||
{
|
||||
Binder = new MockBinder();
|
||||
}
|
||||
|
||||
public override Task ExecuteAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class MockBinder : PageArgumentBinder
|
||||
{
|
||||
protected override Task<ModelBindingResult> BindAsync(PageContext context, object value, string name, Type type)
|
||||
{
|
||||
var result = ModelBindingResult.Failed();
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -171,6 +171,51 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Assert.Equal(new[] { factory2, factory1 }, entry.PageStartFactories);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_CachesExecutor()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor
|
||||
{
|
||||
RelativePath = "/Home/Path1/File.cshtml",
|
||||
ViewEnginePath = "/Home/Path1/File.cshtml",
|
||||
FilterDescriptors = new FilterDescriptor[0],
|
||||
};
|
||||
|
||||
var loader = new Mock<IPageLoader>();
|
||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||
.Returns(typeof(PageWithModel));
|
||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||
var razorPageFactoryProvider = new Mock<IRazorPageFactoryProvider>();
|
||||
var fileProvider = new TestFileProvider();
|
||||
var defaultRazorProject = new DefaultRazorProject(fileProvider);
|
||||
|
||||
var invokerProvider = CreateInvokerProvider(
|
||||
loader.Object,
|
||||
actionDescriptorProvider.Object,
|
||||
razorPageFactoryProvider: razorPageFactoryProvider.Object,
|
||||
razorProject: defaultRazorProject);
|
||||
var context = new ActionInvokerProviderContext(
|
||||
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
||||
|
||||
// Act
|
||||
invokerProvider.OnProvidersExecuting(context);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(context.Result);
|
||||
var actionInvoker = Assert.IsType<PageActionInvoker>(context.Result);
|
||||
var actionDescriptor = actionInvoker.CacheEntry.ActionDescriptor;
|
||||
Assert.Collection(actionDescriptor.HandlerMethods,
|
||||
handlerDescriptor =>
|
||||
{
|
||||
Assert.Equal(nameof(TestPageModel.OnGet), handlerDescriptor.Method.Name);
|
||||
Assert.NotNull(handlerDescriptor.Executor);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void OnProvidersExecuting_CachesEntries()
|
||||
{
|
||||
|
|
@ -291,7 +336,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
private class PageWithModel
|
||||
{
|
||||
public object Model { get; set; }
|
||||
public TestPageModel Model { get; set; }
|
||||
}
|
||||
|
||||
private class TestPageModel
|
||||
{
|
||||
public void OnGet()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue