* 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");
}
/// <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);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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>

View File

@ -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": {

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);
}
[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()
{
}
}
}
}