* Cache ExecutorFactory as part of CompiledActionDescriptor

* Add tests for ExecutorFactory
This commit is contained in:
Pranav K 2017-02-22 16:01:27 -08:00
parent 498c1c570d
commit de30c5822a
10 changed files with 646 additions and 93 deletions

View File

@ -170,38 +170,6 @@ namespace Microsoft.AspNetCore.Mvc.Abstractions
return GetString("BindingSource_Services"); 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> /// <summary>
/// ModelBinding /// ModelBinding
/// </summary> /// </summary>
@ -314,6 +282,38 @@ namespace Microsoft.AspNetCore.Mvc.Abstractions
return string.Format(CultureInfo.CurrentCulture, GetString("BindingSource_MustBeGreedy"), p0, p1); 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) private static string GetString(string name, params string[] formatterNames)
{ {
var value = _resourceManager.GetString(name); var value = _resourceManager.GetString(name);

View File

@ -1,12 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved. // 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. // 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.Reflection;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{ {
public class HandlerMethodDescriptor public class HandlerMethodDescriptor
{ {
public MethodInfo Method { get; set; } public MethodInfo Method { get; set; }
public Func<Page, object, Task<IActionResult>> Executor { get; set; }
} }
} }

View File

@ -5,29 +5,31 @@ 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.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>> Create(MethodInfo method) public static Func<Page, object, Task<IActionResult>> CreateExecutor(
CompiledPageActionDescriptor actionDescriptor,
MethodInfo method)
{ {
return new Executor() if (actionDescriptor == null)
{ {
Method = method, throw new ArgumentNullException(nameof(actionDescriptor));
}.Execute; }
}
private class Executor if (method == null)
{
public MethodInfo Method { get; set; }
public async Task<IActionResult> Execute(Page page, object model)
{ {
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]; var arguments = new object[handler.Parameters.Length];
for (var i = 0; i < handler.Parameters.Length; i++) for (var i = 0; i < handler.Parameters.Length; i++)
{ {
@ -39,68 +41,69 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
parameter.Name); parameter.Name);
} }
var receiver = methodIsDeclaredOnPage ? page : model;
var result = await handler.Execute(receiver, arguments); var result = await handler.Execute(receiver, arguments);
return result; 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 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) protected static Expression[] Unpack(Expression arguments, HandlerParameter[] 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(Expression.ArrayIndex(arguments, Expression.Constant(i)), parameters[i].Type); unpackExpressions[i] = Expression.Convert(
Expression.ArrayIndex(arguments, Expression.Constant(i)),
parameters[i].Type);
} }
return unpackExpressions; return unpackExpressions;
@ -178,7 +181,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
private static async Task<object> Convert<T>(object taskAsObject) private static async Task<object> Convert<T>(object taskAsObject)
{ {
var task = (Task<T>)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)); 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

@ -365,7 +365,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
var handler = _selector.Select(_pageContext); var handler = _selector.Select(_pageContext);
if (handler != null) if (handler != null)
{ {
var executor = ExecutorFactory.Create(handler.Method); var executor = handler.Executor;
result = await executor(_page, _model); result = await executor(_page, _model);
} }

View File

@ -239,6 +239,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
actionDescriptor.HandlerMethods.Add(new HandlerMethodDescriptor() actionDescriptor.HandlerMethods.Add(new HandlerMethodDescriptor()
{ {
Method = method, Method = method,
Executor = ExecutorFactory.CreateExecutor(actionDescriptor, method),
}); });
} }
} }

View File

@ -90,6 +90,22 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
return GetString("ArgumentCannotBeNullOrEmpty"); 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) private static string GetString(string name, params string[] formatterNames)
{ {
var value = _resourceManager.GetString(name); var value = _resourceManager.GetString(name);

View File

@ -132,4 +132,7 @@
<data name="ArgumentCannotBeNullOrEmpty" xml:space="preserve"> <data name="ArgumentCannotBeNullOrEmpty" xml:space="preserve">
<value>Value cannot be null or empty.</value> <value>Value cannot be null or empty.</value>
</data> </data>
<data name="UnsupportedHandlerMethodType" xml:space="preserve">
<value>Unsupported handler method return type '{0}'.</value>
</data>
</root> </root>

View File

@ -33,6 +33,10 @@
"version": "1.2.0-*", "version": "1.2.0-*",
"type": "build" "type": "build"
}, },
"Microsoft.Extensions.ClosedGenericMatcher.Sources": {
"version": "1.2.0-*",
"type": "build"
},
"NETStandard.Library": "1.6.1" "NETStandard.Library": "1.6.1"
}, },
"frameworks": { "frameworks": {

View File

@ -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);
}
}
}
}

View File

@ -171,6 +171,51 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
Assert.Equal(new[] { factory2, factory1 }, entry.PageStartFactories); 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] [Fact]
public void OnProvidersExecuting_CachesEntries() public void OnProvidersExecuting_CachesEntries()
{ {
@ -291,7 +336,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
private class PageWithModel private class PageWithModel
{ {
public object Model { get; set; } public TestPageModel Model { get; set; }
}
private class TestPageModel
{
public void OnGet()
{
}
} }
} }
} }