Remove Binder from Page and PageModel

This commit is contained in:
Ryan Brandenburg 2017-04-19 14:57:34 -07:00 committed by Ryan Nowak
parent f4d84f50cd
commit 5a184df808
10 changed files with 215 additions and 151 deletions

View File

@ -12,10 +12,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{ {
public MethodInfo Method { get; set; } public MethodInfo Method { get; set; }
public Func<Page, object, Task<IActionResult>> Executor { get; set; } public Func<object, object[], Task<IActionResult>> Executor { get; set; }
public string HttpMethod { get; set; } public string HttpMethod { get; set; }
public StringSegment FormAction { get; set; } public StringSegment FormAction { get; set; }
public HandlerParameterDescriptor[] Parameters { get; set; }
public bool OnPage { get; set; }
} }
} }

View File

@ -0,0 +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 Microsoft.AspNetCore.Mvc.Abstractions;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
public class HandlerParameterDescriptor : ParameterDescriptor
{
public object DefaultValue { get; set; }
public ParameterInfo Parameter { get; set; }
}
}

View File

@ -5,15 +5,17 @@ using System;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.Extensions.Internal; using Microsoft.Extensions.Internal;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{ {
public static class ExecutorFactory public static class ExecutorFactory
{ {
public static Func<Page, object, Task<IActionResult>> CreateExecutor( public static Func<object, object[], Task<IActionResult>> CreateExecutor(
CompiledPageActionDescriptor actionDescriptor, CompiledPageActionDescriptor actionDescriptor,
MethodInfo method) MethodInfo method,
HandlerParameterDescriptor[] parameters)
{ {
if (actionDescriptor == null) if (actionDescriptor == null)
{ {
@ -25,48 +27,24 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
throw new ArgumentNullException(nameof(method)); throw new ArgumentNullException(nameof(method));
} }
var methodIsDeclaredOnPage = method.DeclaringType.GetTypeInfo().IsAssignableFrom(actionDescriptor.PageTypeInfo); if (parameters == null)
var handler = CreateHandlerMethod(method);
return async (page, model) =>
{ {
var arguments = new object[handler.Parameters.Length]; throw new ArgumentNullException(nameof(parameters));
for (var i = 0; i < handler.Parameters.Length; i++) }
{
var parameter = handler.Parameters[i];
arguments[i] = await page.Binder.BindModelAsync(
page.PageContext,
parameter.Type,
parameter.DefaultValue,
parameter.Name);
}
var receiver = methodIsDeclaredOnPage ? page : model; var methodIsDeclaredOnPage = method.DeclaringType.GetTypeInfo().IsAssignableFrom(actionDescriptor.PageTypeInfo);
var handler = CreateHandlerMethod(method, parameters);
return async (receiver, arguments) =>
{
var result = await handler.Execute(receiver, arguments); var result = await handler.Execute(receiver, arguments);
return result; return result;
}; };
} }
private static HandlerMethod CreateHandlerMethod(MethodInfo method) private static HandlerMethod CreateHandlerMethod(MethodInfo method, HandlerParameterDescriptor[] parameters)
{ {
var methodParameters = method.GetParameters(); var methodParameters = method.GetParameters();
var parameters = new HandlerParameter[methodParameters.Length];
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);
}
parameters[i] = new HandlerParameter(methodParameter.Name, methodParameter.ParameterType, defaultValue);
}
var returnType = method.ReturnType; var returnType = method.ReturnType;
var returnTypeInfo = method.ReturnType.GetTypeInfo(); var returnTypeInfo = method.ReturnType.GetTypeInfo();
@ -96,25 +74,25 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
private abstract class HandlerMethod private abstract class HandlerMethod
{ {
protected static Expression[] Unpack(Expression arguments, HandlerParameter[] parameters) protected static Expression[] Unpack(Expression arguments, HandlerParameterDescriptor[] parameters)
{ {
var unpackExpressions = new Expression[parameters.Length]; var unpackExpressions = new Expression[parameters.Length];
for (var i = 0; i < parameters.Length; i++) for (var i = 0; i < parameters.Length; i++)
{ {
unpackExpressions[i] = Expression.Convert( unpackExpressions[i] = Expression.Convert(
Expression.ArrayIndex(arguments, Expression.Constant(i)), Expression.ArrayIndex(arguments, Expression.Constant(i)),
parameters[i].Type); parameters[i].ParameterType);
} }
return unpackExpressions; return unpackExpressions;
} }
protected HandlerMethod(HandlerParameter[] parameters) protected HandlerMethod(HandlerParameterDescriptor[] parameters)
{ {
Parameters = parameters; Parameters = parameters;
} }
public HandlerParameter[] Parameters { get; } public HandlerParameterDescriptor[] Parameters { get; }
public abstract Task<IActionResult> Execute(object receiver, object[] arguments); public abstract Task<IActionResult> Execute(object receiver, object[] arguments);
} }
@ -123,7 +101,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{ {
private readonly Func<object, object[], Task> _thunk; private readonly Func<object, object[], Task> _thunk;
public NonGenericTaskHandlerMethod(HandlerParameter[] parameters, MethodInfo method) public NonGenericTaskHandlerMethod(HandlerParameterDescriptor[] parameters, MethodInfo method)
: base(parameters) : base(parameters)
{ {
var receiver = Expression.Parameter(typeof(object), "receiver"); var receiver = Expression.Parameter(typeof(object), "receiver");
@ -153,7 +131,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
private readonly Func<object, object[], Task<object>> _thunk; private readonly Func<object, object[], Task<object>> _thunk;
public GenericTaskHandlerMethod(HandlerParameter[] parameters, MethodInfo method) public GenericTaskHandlerMethod(HandlerParameterDescriptor[] parameters, MethodInfo method)
: base(parameters) : base(parameters)
{ {
var receiver = Expression.Parameter(typeof(object), "receiver"); var receiver = Expression.Parameter(typeof(object), "receiver");
@ -189,7 +167,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{ {
private readonly Action<object, object[]> _thunk; private readonly Action<object, object[]> _thunk;
public VoidHandlerMethod(HandlerParameter[] parameters, MethodInfo method) public VoidHandlerMethod(HandlerParameterDescriptor[] parameters, MethodInfo method)
: base(parameters) : base(parameters)
{ {
var receiver = Expression.Parameter(typeof(object), "receiver"); var receiver = Expression.Parameter(typeof(object), "receiver");
@ -215,7 +193,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{ {
private readonly Func<object, object[], IActionResult> _thunk; private readonly Func<object, object[], IActionResult> _thunk;
public ActionResultHandlerMethod(HandlerParameter[] parameters, MethodInfo method) public ActionResultHandlerMethod(HandlerParameterDescriptor[] parameters, MethodInfo method)
: base(parameters) : base(parameters)
{ {
var receiver = Expression.Parameter(typeof(object), "receiver"); var receiver = Expression.Parameter(typeof(object), "receiver");
@ -237,21 +215,5 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
return Task.FromResult(_thunk(receiver, arguments)); 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; }
}
} }
} }

View File

@ -22,6 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
{ {
private readonly IPageHandlerMethodSelector _selector; private readonly IPageHandlerMethodSelector _selector;
private readonly PageContext _pageContext; private readonly PageContext _pageContext;
private readonly ParameterBinder _parameterBinder;
private Page _page; private Page _page;
private object _model; private object _model;
@ -34,7 +35,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
PageContext pageContext, PageContext pageContext,
IFilterMetadata[] filterMetadata, IFilterMetadata[] filterMetadata,
IList<IValueProviderFactory> valueProviderFactories, IList<IValueProviderFactory> valueProviderFactories,
PageActionInvokerCacheEntry cacheEntry) PageActionInvokerCacheEntry cacheEntry,
ParameterBinder parameterBinder)
: base( : base(
diagnosticSource, diagnosticSource,
logger, logger,
@ -45,6 +47,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
_selector = handlerMethodSelector; _selector = handlerMethodSelector;
_pageContext = pageContext; _pageContext = pageContext;
CacheEntry = cacheEntry; CacheEntry = cacheEntry;
_parameterBinder = parameterBinder;
} }
public PageActionInvokerCacheEntry CacheEntry { get; } public PageActionInvokerCacheEntry CacheEntry { get; }
@ -374,8 +377,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var handler = _selector.Select(_pageContext); var handler = _selector.Select(_pageContext);
if (handler != null) if (handler != null)
{ {
var arguments = await GetArguments(handler);
var executor = handler.Executor; var executor = handler.Executor;
result = await executor(_page, _model); result = await executor(handler.OnPage ? _page : _model, arguments);
} }
if (result == null) if (result == null)
@ -386,6 +391,27 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
await result.ExecuteResultAsync(_pageContext); await result.ExecuteResultAsync(_pageContext);
} }
private async Task<object[]> GetArguments(HandlerMethodDescriptor handler)
{
var arguments = new object[handler.Parameters.Length];
var valueProvider = await CompositeValueProvider.CreateAsync(_pageContext, _pageContext.ValueProviderFactories);
for (var i = 0; i < handler.Parameters.Length; i++)
{
var parameter = handler.Parameters[i];
var result = await _parameterBinder.BindModelAsync(
_page.PageContext,
valueProvider,
parameter,
value: null);
arguments[i] = result.IsModelSet ? result.Model : parameter.DefaultValue;
}
return arguments;
}
private async Task InvokeNextExceptionFilterAsync() private async Task InvokeNextExceptionFilterAsync()
{ {
try try

View File

@ -5,6 +5,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.Razor;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal

View File

@ -15,7 +15,6 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.AspNetCore.Razor.Language; using Microsoft.AspNetCore.Razor.Language;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
@ -162,7 +161,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
pageContext, pageContext,
filters, filters,
new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories), new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories),
cacheEntry); cacheEntry,
_parameterBinder);
} }
private PageActionInvokerCacheEntry CreateCacheEntry( private PageActionInvokerCacheEntry CreateCacheEntry(
@ -264,18 +264,59 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var formAction = new StringSegment(method.Name, formActionStart, formActionLength); var formAction = new StringSegment(method.Name, formActionStart, formActionLength);
var parameters = GetHandlerParameters(method);
var handlerMethodDescriptor = new HandlerMethodDescriptor var handlerMethodDescriptor = new HandlerMethodDescriptor
{ {
Method = method, Method = method,
Executor = ExecutorFactory.CreateExecutor(actionDescriptor, method), Executor = ExecutorFactory.CreateExecutor(actionDescriptor, method, parameters),
FormAction = formAction, FormAction = formAction,
HttpMethod = httpMethod, HttpMethod = httpMethod,
Parameters = parameters,
OnPage = actionDescriptor.PageTypeInfo == type,
}; };
actionDescriptor.HandlerMethods.Add(handlerMethodDescriptor); actionDescriptor.HandlerMethods.Add(handlerMethodDescriptor);
} }
} }
private static HandlerParameterDescriptor[] GetHandlerParameters(MethodInfo methodInfo)
{
var methodParameters = methodInfo.GetParameters();
var parameters = new HandlerParameterDescriptor[methodParameters.Length];
for (var i = 0; i < methodParameters.Length; i++)
{
var parameter = methodParameters[i];
parameters[i] = new HandlerParameterDescriptor()
{
BindingInfo = BindingInfo.GetBindingInfo(parameter.GetCustomAttributes()),
DefaultValue = GetDefaultValue(parameter),
Name = parameter.Name,
Parameter = parameter,
ParameterType = parameter.ParameterType,
};
}
return parameters;
}
private static object GetDefaultValue(ParameterInfo methodParameter)
{
object defaultValue = null;
if (methodParameter.HasDefaultValue)
{
defaultValue = methodParameter.DefaultValue;
}
else if (methodParameter.ParameterType.GetTypeInfo().IsValueType)
{
defaultValue = Activator.CreateInstance(methodParameter.ParameterType);
}
return defaultValue;
}
private static bool IsValidHandler(MethodInfo methodInfo) private static bool IsValidHandler(MethodInfo methodInfo)
{ {
// The SpecialName bit is set to flag members that are treated in a special way by some compilers // The SpecialName bit is set to flag members that are treated in a special way by some compilers

View File

@ -27,7 +27,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
/// </summary> /// </summary>
public abstract class Page : RazorPageBase, IRazorPage public abstract class Page : RazorPageBase, IRazorPage
{ {
private PageArgumentBinder _binder;
private IObjectModelValidator _objectValidator; private IObjectModelValidator _objectValidator;
private IModelMetadataProvider _metadataProvider; private IModelMetadataProvider _metadataProvider;
private IModelBinderFactory _modelBinderFactory; private IModelBinderFactory _modelBinderFactory;
@ -62,32 +61,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
/// </summary> /// </summary>
public HttpResponse Response => HttpContext?.Response; public HttpResponse Response => HttpContext?.Response;
/// <summary>
/// Gets or sets the <see cref="PageArgumentBinder"/>.
/// </summary>
public PageArgumentBinder Binder
{
get
{
if (_binder == null)
{
_binder = PageContext.HttpContext.RequestServices.GetRequiredService<PageArgumentBinder>();
}
return _binder;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_binder = value;
}
}
/// <summary> /// <summary>
/// Gets the <see cref="AspNetCore.Routing.RouteData"/> for the executing action. /// Gets the <see cref="AspNetCore.Routing.RouteData"/> for the executing action.
/// </summary> /// </summary>

View File

@ -16,7 +16,6 @@ using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
@ -25,38 +24,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
{ {
public abstract class PageModel public abstract class PageModel
{ {
private PageArgumentBinder _binder;
private IObjectModelValidator _objectValidator; private IObjectModelValidator _objectValidator;
private IModelMetadataProvider _metadataProvider; private IModelMetadataProvider _metadataProvider;
private IModelBinderFactory _modelBinderFactory; private IModelBinderFactory _modelBinderFactory;
private IUrlHelper _urlHelper; private IUrlHelper _urlHelper;
/// <summary>
/// Gets or sets the <see cref="PageArgumentBinder"/>.
/// </summary>
public PageArgumentBinder Binder
{
get
{
if (_binder == null)
{
_binder = HttpContext?.RequestServices?.GetRequiredService<PageArgumentBinder>();
}
return _binder;
}
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_binder = value;
}
}
/// <summary> /// <summary>
/// Gets or sets the <see cref="IUrlHelper"/>. /// Gets or sets the <see cref="IUrlHelper"/>.
/// </summary> /// </summary>

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
@ -25,7 +26,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ActionResultReturningHandler)); var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ActionResultReturningHandler));
// Act // Act
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
// Assert // Assert
Assert.NotNull(executor); Assert.NotNull(executor);
@ -45,7 +46,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ConcreteActionResult)); var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ConcreteActionResult));
// Act // Act
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
// Assert // Assert
Assert.NotNull(executor); Assert.NotNull(executor);
@ -63,13 +64,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
PageTypeInfo = typeof(TestPage).GetTypeInfo(), PageTypeInfo = typeof(TestPage).GetTypeInfo(),
}; };
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ActionResultReturnHandlerWithParameters)); var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.ActionResultReturnHandlerWithParameters));
var parameters = CreateParameters(methodInfo);
// Act // Act
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, parameters);
// Assert // Assert
Assert.NotNull(executor); Assert.NotNull(executor);
var actionResultTask = executor(new TestPage(), null); var actionResultTask = executor(new TestPage(), CreateArguments(methodInfo));
var actionResult = await actionResultTask; var actionResult = await actionResultTask;
var contentResult = Assert.IsType<ContentResult>(actionResult); var contentResult = Assert.IsType<ContentResult>(actionResult);
Assert.Equal("Hello 0", contentResult.Content); Assert.Equal("Hello 0", contentResult.Content);
@ -87,7 +89,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.VoidReturningHandler)); var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.VoidReturningHandler));
// Act // Act
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
// Assert // Assert
Assert.NotNull(executor); Assert.NotNull(executor);
@ -109,7 +111,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.VoidTaskReturningHandler)); var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.VoidTaskReturningHandler));
// Act // Act
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
// Assert // Assert
Assert.NotNull(executor); Assert.NotNull(executor);
@ -130,7 +132,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.GenericTaskHandler)); var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.GenericTaskHandler));
// Act // Act
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo));
// Assert // Assert
Assert.NotNull(executor); Assert.NotNull(executor);
@ -150,11 +152,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.TaskReturningConcreteSubtype)); var methodInfo = typeof(TestPage).GetMethod(nameof(TestPage.TaskReturningConcreteSubtype));
// Act // Act
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo));
// Assert // Assert
Assert.NotNull(executor); Assert.NotNull(executor);
var actionResultTask = executor(new TestPage(), null); var actionResultTask = executor(new TestPage(), CreateArguments(methodInfo));
var actionResult = await actionResultTask; var actionResult = await actionResultTask;
var contentResult = Assert.IsType<ContentResult>(actionResult); var contentResult = Assert.IsType<ContentResult>(actionResult);
Assert.Equal("value", contentResult.Content); Assert.Equal("value", contentResult.Content);
@ -172,11 +174,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.ActionResultReturningHandler)); var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.ActionResultReturningHandler));
// Act // Act
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
// Assert // Assert
Assert.NotNull(executor); Assert.NotNull(executor);
var actionResultTask = executor(new EmptyPage(), new TestPageModel()); var actionResultTask = executor(new TestPageModel(), null);
var actionResult = await actionResultTask; var actionResult = await actionResultTask;
Assert.IsType<EmptyResult>(actionResult); Assert.IsType<EmptyResult>(actionResult);
} }
@ -193,11 +195,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.ConcreteActionResult)); var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.ConcreteActionResult));
// Act // Act
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo));
// Assert // Assert
Assert.NotNull(executor); Assert.NotNull(executor);
var actionResultTask = executor(new EmptyPage(), new TestPageModel()); var actionResultTask = executor(new TestPageModel(), null);
var actionResult = await actionResultTask; var actionResult = await actionResultTask;
Assert.IsType<ViewResult>(actionResult); Assert.IsType<ViewResult>(actionResult);
} }
@ -214,11 +216,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.ActionResultReturnHandlerWithParameters)); var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.ActionResultReturnHandlerWithParameters));
// Act // Act
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo));
// Assert // Assert
Assert.NotNull(executor); Assert.NotNull(executor);
var actionResultTask = executor(new EmptyPage(), new TestPageModel()); var actionResultTask = executor(new TestPageModel(), CreateArguments(methodInfo));
var actionResult = await actionResultTask; var actionResult = await actionResultTask;
var contentResult = Assert.IsType<ContentResult>(actionResult); var contentResult = Assert.IsType<ContentResult>(actionResult);
Assert.Equal("Hello 0", contentResult.Content); Assert.Equal("Hello 0", contentResult.Content);
@ -237,11 +239,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.VoidReturningHandler)); var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.VoidReturningHandler));
// Act // Act
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
// Assert // Assert
Assert.NotNull(executor); Assert.NotNull(executor);
var actionResultTask = executor(new EmptyPage(), model); var actionResultTask = executor(model, null);
var actionResult = await actionResultTask; var actionResult = await actionResultTask;
Assert.Null(actionResult); Assert.Null(actionResult);
Assert.True(model.SideEffects); Assert.True(model.SideEffects);
@ -260,11 +262,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.VoidTaskReturningHandler)); var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.VoidTaskReturningHandler));
// Act // Act
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]);
// Assert // Assert
Assert.NotNull(executor); Assert.NotNull(executor);
var actionResultTask = executor(new EmptyPage(), model); var actionResultTask = executor(model, null);
var actionResult = await actionResultTask; var actionResult = await actionResultTask;
Assert.Null(actionResult); Assert.Null(actionResult);
Assert.True(model.SideEffects); Assert.True(model.SideEffects);
@ -282,11 +284,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.GenericTaskHandler)); var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.GenericTaskHandler));
// Act // Act
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo));
// Assert // Assert
Assert.NotNull(executor); Assert.NotNull(executor);
var actionResultTask = executor(new EmptyPage(), new TestPageModel()); var actionResultTask = executor(new TestPageModel(), null);
var actionResult = await actionResultTask; var actionResult = await actionResultTask;
Assert.IsType<EmptyResult>(actionResult); Assert.IsType<EmptyResult>(actionResult);
} }
@ -303,11 +305,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.TaskReturningConcreteSubtype)); var methodInfo = typeof(TestPageModel).GetMethod(nameof(TestPageModel.TaskReturningConcreteSubtype));
// Act // Act
var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo); var executor = ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, CreateParameters(methodInfo));
// Assert // Assert
Assert.NotNull(executor); Assert.NotNull(executor);
var actionResultTask = executor(new EmptyPage(), new TestPageModel()); var actionResultTask = executor(new TestPageModel(), CreateArguments(methodInfo));
var actionResult = await actionResultTask; var actionResult = await actionResultTask;
var contentResult = Assert.IsType<ContentResult>(actionResult); var contentResult = Assert.IsType<ContentResult>(actionResult);
Assert.Equal("value", contentResult.Content); Assert.Equal("value", contentResult.Content);
@ -328,15 +330,50 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
var methodInfo = typeof(TestPageModel).GetMethod(methodName); var methodInfo = typeof(TestPageModel).GetMethod(methodName);
// Act & Assert // Act & Assert
var ex = Assert.Throws<InvalidOperationException>(() => ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo)); var ex = Assert.Throws<InvalidOperationException>(() =>
ExecutorFactory.CreateExecutor(actionDescriptor, methodInfo, new HandlerParameterDescriptor[0]));
Assert.Equal($"Unsupported handler method return type '{methodInfo.ReturnType}'.", ex.Message); Assert.Equal($"Unsupported handler method return type '{methodInfo.ReturnType}'.", ex.Message);
} }
private static object[] CreateArguments(MethodInfo methodInfo)
{
var parameters = methodInfo.GetParameters();
return parameters.Select(s => GetDefaultValue(s)).ToArray();
}
private static object GetDefaultValue(ParameterInfo methodParameter)
{
object defaultValue = null;
if (methodParameter.HasDefaultValue)
{
defaultValue = methodParameter.DefaultValue;
}
else if (methodParameter.ParameterType.GetTypeInfo().IsValueType)
{
defaultValue = Activator.CreateInstance(methodParameter.ParameterType);
}
return defaultValue;
}
private static HandlerParameterDescriptor[] CreateParameters(MethodInfo methodInfo)
{
var parameters = methodInfo.GetParameters();
return parameters.Select(p => new HandlerParameterDescriptor()
{
BindingInfo = BindingInfo.GetBindingInfo(p.GetCustomAttributes()),
Name = p.Name,
Parameter = p,
ParameterType = p.ParameterType,
}).ToArray();
}
private class TestPage : Page private class TestPage : Page
{ {
public TestPage() public TestPage()
{ {
Binder = new MockBinder();
} }
public bool SideEffects { get; private set; } public bool SideEffects { get; private set; }
@ -433,7 +470,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Test.Internal
{ {
public EmptyPage() public EmptyPage()
{ {
Binder = new MockBinder();
} }
public override Task ExecuteAsync() public override Task ExecuteAsync()

View File

@ -11,11 +11,11 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Internal; using Microsoft.AspNetCore.Mvc.Internal;
using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
using Microsoft.AspNetCore.Mvc.Razor; using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure; using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
using Microsoft.AspNetCore.Mvc.ViewEngines; using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
using Microsoft.AspNetCore.Routing; using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -608,7 +608,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
null, null,
null, null,
new FilterItem[0]); new FilterItem[0]);
var invoker = new PageActionInvoker( var invoker = new PageActionInvoker(
selector, selector,
diagnosticSource, diagnosticSource,
@ -616,10 +616,43 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
pageContext, pageContext,
filters, filters,
valueProviderFactories.AsReadOnly(), valueProviderFactories.AsReadOnly(),
cacheEntry); cacheEntry,
GetParameterBinder());
return invoker; return invoker;
} }
private static ParameterBinder GetParameterBinder(
IModelBinderFactory factory = null,
IObjectModelValidator validator = null)
{
if (validator == null)
{
validator = CreateMockValidator();
}
if (factory == null)
{
factory = TestModelBinderFactory.CreateDefault();
}
return new ParameterBinder(
TestModelMetadataProvider.CreateDefaultProvider(),
factory,
validator);
}
private static IObjectModelValidator CreateMockValidator()
{
var mockValidator = new Mock<IObjectModelValidator>(MockBehavior.Strict);
mockValidator
.Setup(o => o.Validate(
It.IsAny<ActionContext>(),
It.IsAny<ValidationStateDictionary>(),
It.IsAny<string>(),
It.IsAny<object>()));
return mockValidator.Object;
}
private class TestPageResultExecutor : PageResultExecutor private class TestPageResultExecutor : PageResultExecutor
{ {
private readonly Func<PageContext, Task> _executeAction; private readonly Func<PageContext, Task> _executeAction;