Separate PageContext and ViewContext
This change decouples PageContext and ViewContext completely.
This commit is contained in:
parent
6094c6ff52
commit
2992f8e38a
|
|
@ -1285,7 +1285,7 @@ namespace Microsoft.AspNetCore.Mvc.Core
|
|||
=> string.Format(CultureInfo.CurrentCulture, GetString("NoRoutesMatchedForPage"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// The relative page path '{0}' can only can only be used while executing a Razor Page. Specify a root relative path with a leading '/' to generate a URL outside of a Razor Page.
|
||||
/// The relative page path '{0}' can only be used while executing a Razor Page. Specify a root relative path with a leading '/' to generate a URL outside of a Razor Page.
|
||||
/// </summary>
|
||||
internal static string UrlHelper_RelativePagePathIsNotSupported
|
||||
{
|
||||
|
|
@ -1293,7 +1293,7 @@ namespace Microsoft.AspNetCore.Mvc.Core
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// The relative page path '{0}' can only can only be used while executing a Razor Page. Specify a root relative path with a leading '/' to generate a URL outside of a Razor Page.
|
||||
/// The relative page path '{0}' can only be used while executing a Razor Page. Specify a root relative path with a leading '/' to generate a URL outside of a Razor Page.
|
||||
/// </summary>
|
||||
internal static string FormatUrlHelper_RelativePagePathIsNotSupported(object p0)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("UrlHelper_RelativePagePathIsNotSupported"), p0);
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@
|
|||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text.Encodings.Web;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Internal;
|
||||
|
||||
|
|
@ -16,8 +16,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
|||
{
|
||||
public class RazorPagePropertyActivator
|
||||
{
|
||||
private delegate ViewDataDictionary CreateViewDataNestedDelegate(ViewDataDictionary source);
|
||||
private delegate ViewDataDictionary CreateViewDataRootDelegate(ModelStateDictionary modelState);
|
||||
private readonly IModelMetadataProvider _metadataProvider;
|
||||
private readonly Func<IModelMetadataProvider, ModelStateDictionary, ViewDataDictionary> _rootFactory;
|
||||
private readonly Func<ViewDataDictionary, ViewDataDictionary> _nestedFactory;
|
||||
private readonly Type _viewDataDictionaryType;
|
||||
private readonly PropertyActivator<ViewContext>[] _propertyActivators;
|
||||
|
||||
public RazorPagePropertyActivator(
|
||||
Type pageType,
|
||||
|
|
@ -25,26 +28,19 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
|||
IModelMetadataProvider metadataProvider,
|
||||
PropertyValueAccessors propertyValueAccessors)
|
||||
{
|
||||
var viewDataType = typeof(ViewDataDictionary<>).MakeGenericType(modelType);
|
||||
ViewDataDictionaryType = viewDataType;
|
||||
CreateViewDataNested = GetCreateViewDataNested(viewDataType);
|
||||
CreateViewDataRoot = GetCreateViewDataRoot(viewDataType, metadataProvider);
|
||||
_metadataProvider = metadataProvider;
|
||||
|
||||
PropertyActivators = PropertyActivator<ViewContext>.GetPropertiesToActivate(
|
||||
_viewDataDictionaryType = typeof(ViewDataDictionary<>).MakeGenericType(modelType);
|
||||
_rootFactory = ViewDataDictionaryFactory.CreateFactory(modelType.GetTypeInfo());
|
||||
_nestedFactory = ViewDataDictionaryFactory.CreateNestedFactory(modelType.GetTypeInfo());
|
||||
|
||||
_propertyActivators = PropertyActivator<ViewContext>.GetPropertiesToActivate(
|
||||
pageType,
|
||||
typeof(RazorInjectAttribute),
|
||||
propertyInfo => CreateActivateInfo(propertyInfo, propertyValueAccessors),
|
||||
includeNonPublic: true);
|
||||
}
|
||||
|
||||
private PropertyActivator<ViewContext>[] PropertyActivators { get; }
|
||||
|
||||
private Type ViewDataDictionaryType { get; }
|
||||
|
||||
private CreateViewDataNestedDelegate CreateViewDataNested { get; }
|
||||
|
||||
private CreateViewDataRootDelegate CreateViewDataRoot { get; }
|
||||
|
||||
public void Activate(object page, ViewContext context)
|
||||
{
|
||||
if (context == null)
|
||||
|
|
@ -54,9 +50,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
|||
|
||||
context.ViewData = CreateViewDataDictionary(context);
|
||||
|
||||
for (var i = 0; i < PropertyActivators.Length; i++)
|
||||
for (var i = 0; i < _propertyActivators.Length; i++)
|
||||
{
|
||||
var activateInfo = PropertyActivators[i];
|
||||
var activateInfo = _propertyActivators[i];
|
||||
activateInfo.Activate(page, context);
|
||||
}
|
||||
}
|
||||
|
|
@ -68,58 +64,17 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Internal
|
|||
if (context.ViewData == null)
|
||||
{
|
||||
// Create ViewDataDictionary<TModel>(IModelMetadataProvider, ModelStateDictionary).
|
||||
return CreateViewDataRoot(context.ModelState);
|
||||
return _rootFactory(_metadataProvider, context.ModelState);
|
||||
}
|
||||
else if (context.ViewData.GetType() != ViewDataDictionaryType)
|
||||
else if (context.ViewData.GetType() != _viewDataDictionaryType)
|
||||
{
|
||||
// Create ViewDataDictionary<TModel>(ViewDataDictionary).
|
||||
return CreateViewDataNested(context.ViewData);
|
||||
return _nestedFactory(context.ViewData);
|
||||
}
|
||||
|
||||
return context.ViewData;
|
||||
}
|
||||
|
||||
private static CreateViewDataNestedDelegate GetCreateViewDataNested(Type viewDataDictionaryType)
|
||||
{
|
||||
var parameterTypes = new Type[] { typeof(ViewDataDictionary) };
|
||||
var matchingConstructor = viewDataDictionaryType.GetConstructor(parameterTypes);
|
||||
Debug.Assert(matchingConstructor != null);
|
||||
|
||||
var parameters = new ParameterExpression[] { Expression.Parameter(parameterTypes[0]) };
|
||||
var newExpression = Expression.New(matchingConstructor, parameters);
|
||||
var castNewCall = Expression.Convert(
|
||||
newExpression,
|
||||
typeof(ViewDataDictionary));
|
||||
var lambda = Expression.Lambda<CreateViewDataNestedDelegate>(castNewCall, parameters);
|
||||
return lambda.Compile();
|
||||
}
|
||||
|
||||
private static CreateViewDataRootDelegate GetCreateViewDataRoot(
|
||||
Type viewDataDictionaryType,
|
||||
IModelMetadataProvider provider)
|
||||
{
|
||||
var parameterTypes = new[]
|
||||
{
|
||||
typeof(IModelMetadataProvider),
|
||||
typeof(ModelStateDictionary)
|
||||
};
|
||||
var matchingConstructor = viewDataDictionaryType.GetConstructor(parameterTypes);
|
||||
Debug.Assert(matchingConstructor != null);
|
||||
|
||||
var parameterExpression = Expression.Parameter(parameterTypes[1]);
|
||||
var parameters = new Expression[]
|
||||
{
|
||||
Expression.Constant(provider),
|
||||
parameterExpression
|
||||
};
|
||||
var newExpression = Expression.New(matchingConstructor, parameters);
|
||||
var castNewCall = Expression.Convert(
|
||||
newExpression,
|
||||
typeof(ViewDataDictionary));
|
||||
var lambda = Expression.Lambda<CreateViewDataRootDelegate>(castNewCall, parameterExpression);
|
||||
return lambda.Compile();
|
||||
}
|
||||
|
||||
private static PropertyActivator<ViewContext> CreateActivateInfo(
|
||||
PropertyInfo property,
|
||||
PropertyValueAccessors valueAccessors)
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
services.TryAddSingleton<IPageModelActivatorProvider, DefaultPageModelActivatorProvider>();
|
||||
services.TryAddSingleton<IPageModelFactoryProvider, DefaultPageModelFactoryProvider>();
|
||||
|
||||
services.TryAddSingleton<IPageActivatorProvider, DefaultPageActivator>();
|
||||
services.TryAddSingleton<IPageActivatorProvider, DefaultPageActivatorProvider>();
|
||||
services.TryAddSingleton<IPageFactoryProvider, DefaultPageFactory>();
|
||||
|
||||
services.TryAddSingleton<IPageLoader, DefaultPageLoader>();
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||
{
|
||||
|
|
@ -15,13 +16,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
/// </summary>
|
||||
/// <param name="descriptor">The <see cref="CompiledPageActionDescriptor"/>.</param>
|
||||
/// <returns>The delegate used to activate the page.</returns>
|
||||
Func<PageContext, object> CreateActivator(CompiledPageActionDescriptor descriptor);
|
||||
Func<PageContext, ViewContext, object> CreateActivator(CompiledPageActionDescriptor descriptor);
|
||||
|
||||
/// <summary>
|
||||
/// Releases a Razor page.
|
||||
/// </summary>
|
||||
/// <param name="descriptor">The <see cref="CompiledPageActionDescriptor"/>.</param>
|
||||
/// <returns>The delegate used to dispose the activated page.</returns>
|
||||
Action<PageContext, object> CreateReleaser(CompiledPageActionDescriptor descriptor);
|
||||
Action<PageContext, ViewContext, object> CreateReleaser(CompiledPageActionDescriptor descriptor);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||
{
|
||||
|
|
@ -15,13 +16,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
/// </summary>
|
||||
/// <param name="descriptor">The <see cref="CompiledPageActionDescriptor"/>.</param>
|
||||
/// <returns>The Razor page factory.</returns>
|
||||
Func<PageContext, object> CreatePageFactory(CompiledPageActionDescriptor descriptor);
|
||||
Func<PageContext, ViewContext, object> CreatePageFactory(CompiledPageActionDescriptor descriptor);
|
||||
|
||||
/// <summary>
|
||||
/// Releases a Razor page.
|
||||
/// </summary>
|
||||
/// <param name="descriptor">The <see cref="CompiledPageActionDescriptor"/>.</param>
|
||||
/// <returns>The delegate used to release the created page.</returns>
|
||||
Action<PageContext, object> CreatePageDisposer(CompiledPageActionDescriptor descriptor);
|
||||
Action<PageContext, ViewContext, object> CreatePageDisposer(CompiledPageActionDescriptor descriptor);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,18 +4,19 @@
|
|||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="IPageActivatorProvider"/> that uses type activation to create Pages.
|
||||
/// </summary>
|
||||
public class DefaultPageActivator : IPageActivatorProvider
|
||||
public class DefaultPageActivatorProvider : IPageActivatorProvider
|
||||
{
|
||||
private readonly Action<PageContext, object> _disposer = Dispose;
|
||||
private readonly Action<PageContext, ViewContext, object> _disposer = Dispose;
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual Func<PageContext, object> CreateActivator(CompiledPageActionDescriptor actionDescriptor)
|
||||
public virtual Func<PageContext, ViewContext, object> CreateActivator(CompiledPageActionDescriptor actionDescriptor)
|
||||
{
|
||||
if (actionDescriptor == null)
|
||||
{
|
||||
|
|
@ -34,7 +35,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
return CreatePageFactory(pageTypeInfo);
|
||||
}
|
||||
|
||||
public virtual Action<PageContext, object> CreateReleaser(CompiledPageActionDescriptor actionDescriptor)
|
||||
public virtual Action<PageContext, ViewContext, object> CreateReleaser(CompiledPageActionDescriptor actionDescriptor)
|
||||
{
|
||||
if (actionDescriptor == null)
|
||||
{
|
||||
|
|
@ -49,27 +50,33 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
return null;
|
||||
}
|
||||
|
||||
private static Func<PageContext, object> CreatePageFactory(Type pageTypeInfo)
|
||||
private static Func<PageContext, ViewContext, object> CreatePageFactory(Type pageTypeInfo)
|
||||
{
|
||||
var parameter = Expression.Parameter(typeof(PageContext), "pageContext");
|
||||
var parameter1 = Expression.Parameter(typeof(PageContext), "pageContext");
|
||||
var parameter2 = Expression.Parameter(typeof(ViewContext), "viewContext");
|
||||
|
||||
// new Page();
|
||||
var newExpression = Expression.New(pageTypeInfo);
|
||||
|
||||
// () => new Page();
|
||||
var pageFactory = Expression
|
||||
.Lambda<Func<PageContext, object>>(newExpression, parameter)
|
||||
.Lambda<Func<PageContext, ViewContext, object>>(newExpression, parameter1, parameter2)
|
||||
.Compile();
|
||||
return pageFactory;
|
||||
}
|
||||
|
||||
private static void Dispose(PageContext context, object page)
|
||||
private static void Dispose(PageContext context, ViewContext viewContext, object page)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (viewContext == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(viewContext));
|
||||
}
|
||||
|
||||
if (page == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(page));
|
||||
|
|
@ -77,20 +84,5 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
|
||||
((IDisposable)page).Dispose();
|
||||
}
|
||||
|
||||
private static void NullDisposer(PageContext context, object page)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (page == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(page));
|
||||
}
|
||||
|
||||
// No-op
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
};
|
||||
}
|
||||
|
||||
public virtual Func<PageContext, object> CreatePageFactory(CompiledPageActionDescriptor actionDescriptor)
|
||||
public virtual Func<PageContext, ViewContext, object> CreatePageFactory(CompiledPageActionDescriptor actionDescriptor)
|
||||
{
|
||||
if (!typeof(Page).GetTypeInfo().IsAssignableFrom(actionDescriptor.PageTypeInfo))
|
||||
{
|
||||
|
|
@ -57,17 +57,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
_modelMetadataProvider,
|
||||
_propertyAccessors);
|
||||
|
||||
return (context) =>
|
||||
return (pageContext, viewContext) =>
|
||||
{
|
||||
var page = (Page)activatorFactory(context);
|
||||
page.PageContext = context;
|
||||
page.Path = context.ActionDescriptor.RelativePath;
|
||||
propertyActivator.Activate(page, context);
|
||||
var page = (Page)activatorFactory(pageContext, viewContext);
|
||||
page.PageContext = pageContext;
|
||||
page.Path = pageContext.ActionDescriptor.RelativePath;
|
||||
page.ViewContext = viewContext;
|
||||
propertyActivator.Activate(page, viewContext);
|
||||
return page;
|
||||
};
|
||||
}
|
||||
|
||||
public virtual Action<PageContext, object> CreatePageDisposer(CompiledPageActionDescriptor descriptor)
|
||||
public virtual Action<PageContext, ViewContext, object> CreatePageDisposer(CompiledPageActionDescriptor descriptor)
|
||||
{
|
||||
if (descriptor == null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Internal;
|
||||
|
|
@ -65,9 +66,21 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
pageContext.ViewData.Model = result.Model;
|
||||
}
|
||||
|
||||
var view = new RazorView(_razorViewEngine, _razorPageActivator, pageContext.ViewStarts, result.Page, _htmlEncoder);
|
||||
pageContext.View = view;
|
||||
return ExecuteAsync(pageContext, result.ContentType, result.StatusCode);
|
||||
var viewStarts = new IRazorPage[pageContext.ViewStartFactories.Count];
|
||||
for (var i = 0; i < pageContext.ViewStartFactories.Count; i++)
|
||||
{
|
||||
viewStarts[i] = pageContext.ViewStartFactories[i]();
|
||||
}
|
||||
|
||||
var viewContext = result.Page.ViewContext;
|
||||
viewContext.View = new RazorView(
|
||||
_razorViewEngine,
|
||||
_razorPageActivator,
|
||||
viewStarts,
|
||||
result.Page,
|
||||
_htmlEncoder);
|
||||
|
||||
return ExecuteAsync(viewContext, result.ContentType, result.StatusCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -13,8 +14,11 @@ using Microsoft.AspNetCore.Mvc.Internal;
|
|||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
|
|
@ -23,9 +27,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
private readonly IPageHandlerMethodSelector _selector;
|
||||
private readonly PageContext _pageContext;
|
||||
private readonly ParameterBinder _parameterBinder;
|
||||
private readonly ITempDataDictionaryFactory _tempDataFactory;
|
||||
private readonly HtmlHelperOptions _htmlHelperOptions;
|
||||
|
||||
private CompiledPageActionDescriptor _actionDescriptor;
|
||||
private Page _page;
|
||||
private object _model;
|
||||
private ViewContext _viewContext;
|
||||
private ExceptionContext _exceptionContext;
|
||||
|
||||
public PageActionInvoker(
|
||||
|
|
@ -36,7 +44,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
IFilterMetadata[] filterMetadata,
|
||||
IList<IValueProviderFactory> valueProviderFactories,
|
||||
PageActionInvokerCacheEntry cacheEntry,
|
||||
ParameterBinder parameterBinder)
|
||||
ParameterBinder parameterBinder,
|
||||
ITempDataDictionaryFactory tempDataFactory,
|
||||
HtmlHelperOptions htmlHelperOptions)
|
||||
: base(
|
||||
diagnosticSource,
|
||||
logger,
|
||||
|
|
@ -48,9 +58,17 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
_pageContext = pageContext;
|
||||
CacheEntry = cacheEntry;
|
||||
_parameterBinder = parameterBinder;
|
||||
_tempDataFactory = tempDataFactory;
|
||||
_htmlHelperOptions = htmlHelperOptions;
|
||||
|
||||
_actionDescriptor = pageContext.ActionDescriptor;
|
||||
}
|
||||
|
||||
public PageActionInvokerCacheEntry CacheEntry { get; }
|
||||
// Internal for testing
|
||||
internal PageActionInvokerCacheEntry CacheEntry { get; }
|
||||
|
||||
// Internal for testing
|
||||
internal PageContext PageContext => _pageContext;
|
||||
|
||||
/// <remarks>
|
||||
/// <see cref="ResourceInvoker"/> for details on what the variables in this method represent.
|
||||
|
|
@ -77,7 +95,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
if (_page != null && CacheEntry.ReleasePage != null)
|
||||
{
|
||||
CacheEntry.ReleasePage(_pageContext, _page);
|
||||
CacheEntry.ReleasePage(_pageContext, _viewContext, _page);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -303,47 +321,36 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
}
|
||||
}
|
||||
|
||||
private async Task ExecutePageAsync()
|
||||
private Task ExecutePageAsync()
|
||||
{
|
||||
var actionDescriptor = _pageContext.ActionDescriptor;
|
||||
_page = (Page)CacheEntry.PageFactory(_pageContext);
|
||||
_pageContext.Page = _page;
|
||||
_pageContext.ValueProviderFactories = _valueProviderFactories;
|
||||
|
||||
IRazorPage[] viewStarts;
|
||||
|
||||
if (CacheEntry.ViewStartFactories == null || CacheEntry.ViewStartFactories.Count == 0)
|
||||
// There's a fork in the road here between the case where we have a full-fledged PageModel
|
||||
// vs just a Page. We need to know up front because we want to execute handler methods
|
||||
// on the PageModel without instantiating the Page or ViewContext.
|
||||
var hasPageModel = _actionDescriptor.HandlerTypeInfo != _actionDescriptor.PageTypeInfo;
|
||||
if (hasPageModel)
|
||||
{
|
||||
viewStarts = Array.Empty<IRazorPage>();
|
||||
return ExecutePageWithPageModelAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
viewStarts = new IRazorPage[CacheEntry.ViewStartFactories.Count];
|
||||
for (var i = 0; i < viewStarts.Length; i++)
|
||||
{
|
||||
var pageFactory = CacheEntry.ViewStartFactories[i];
|
||||
viewStarts[i] = pageFactory();
|
||||
}
|
||||
return ExecutePageWithoutPageModelAsync();
|
||||
}
|
||||
_pageContext.ViewStarts = viewStarts;
|
||||
}
|
||||
|
||||
if (actionDescriptor.ModelTypeInfo == actionDescriptor.PageTypeInfo)
|
||||
{
|
||||
_model = _page;
|
||||
}
|
||||
else
|
||||
{
|
||||
_model = CacheEntry.ModelFactory(_pageContext);
|
||||
}
|
||||
|
||||
if (_model != null)
|
||||
{
|
||||
_pageContext.ViewData.Model = _model;
|
||||
}
|
||||
private async Task ExecutePageWithPageModelAsync()
|
||||
{
|
||||
// Since this is a PageModel, we need to activate it, and then run a handler method on the model.
|
||||
//
|
||||
// We also know that the model is the pagemodel at this point.
|
||||
Debug.Assert(_actionDescriptor.ModelTypeInfo == _actionDescriptor.HandlerTypeInfo);
|
||||
_model = CacheEntry.ModelFactory(_pageContext);
|
||||
_pageContext.ViewData.Model = _model;
|
||||
|
||||
if (CacheEntry.PropertyBinder != null)
|
||||
{
|
||||
await CacheEntry.PropertyBinder(_page, _model);
|
||||
await CacheEntry.PropertyBinder(_pageContext, _model);
|
||||
}
|
||||
|
||||
// This is a workaround for not yet having proper filter for Pages.
|
||||
|
|
@ -359,44 +366,82 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
if (propertyFilter != null)
|
||||
{
|
||||
object subject = _page;
|
||||
|
||||
if (_model != null)
|
||||
{
|
||||
subject = _model;
|
||||
}
|
||||
|
||||
propertyFilter.Subject = subject;
|
||||
propertyFilter.Subject = _model;
|
||||
propertyFilter.ApplyTempDataChanges(_pageContext.HttpContext);
|
||||
}
|
||||
|
||||
IActionResult result = null;
|
||||
|
||||
var handler = _selector.Select(_pageContext);
|
||||
if (handler != null)
|
||||
_result = await ExecuteHandlerMethod(_model);
|
||||
if (_result is PageResult pageResult)
|
||||
{
|
||||
var arguments = await GetArguments(handler);
|
||||
// If we get here, we are going to render the page, so we need to create it and then initialize
|
||||
// the context so we can run the result.
|
||||
_viewContext = new ViewContext(
|
||||
_pageContext,
|
||||
NullView.Instance,
|
||||
_pageContext.ViewData,
|
||||
_tempDataFactory.GetTempData(_pageContext.HttpContext),
|
||||
TextWriter.Null,
|
||||
_htmlHelperOptions);
|
||||
|
||||
Func<object, object[], Task<IActionResult>> executor = null;
|
||||
for (var i = 0; i < actionDescriptor.HandlerMethods.Count; i++)
|
||||
_page = (Page)CacheEntry.PageFactory(_pageContext, _viewContext);
|
||||
|
||||
pageResult.Page = _page;
|
||||
pageResult.ViewData = pageResult.ViewData ?? _pageContext.ViewData;
|
||||
}
|
||||
|
||||
await _result.ExecuteResultAsync(_pageContext);
|
||||
}
|
||||
|
||||
private async Task ExecutePageWithoutPageModelAsync()
|
||||
{
|
||||
// Since this is a Page without a PageModel, we need to create the Page before running a handler method.
|
||||
_viewContext = new ViewContext(
|
||||
_pageContext,
|
||||
NullView.Instance,
|
||||
_pageContext.ViewData,
|
||||
_tempDataFactory.GetTempData(_pageContext.HttpContext),
|
||||
TextWriter.Null,
|
||||
_htmlHelperOptions);
|
||||
|
||||
_page = (Page)CacheEntry.PageFactory(_pageContext, _viewContext);
|
||||
|
||||
if (_actionDescriptor.ModelTypeInfo == _actionDescriptor.PageTypeInfo)
|
||||
{
|
||||
_model = _page;
|
||||
_pageContext.ViewData.Model = _model;
|
||||
}
|
||||
|
||||
if (CacheEntry.PropertyBinder != null)
|
||||
{
|
||||
await CacheEntry.PropertyBinder(_pageContext, _model);
|
||||
}
|
||||
|
||||
// This is a workaround for not yet having proper filter for Pages.
|
||||
PageSaveTempDataPropertyFilter propertyFilter = null;
|
||||
for (var i = 0; i < _filters.Length; i++)
|
||||
{
|
||||
propertyFilter = _filters[i] as PageSaveTempDataPropertyFilter;
|
||||
if (propertyFilter != null)
|
||||
{
|
||||
if (object.ReferenceEquals(handler, actionDescriptor.HandlerMethods[i]))
|
||||
{
|
||||
executor = CacheEntry.Executors[i];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
var instance = actionDescriptor.ModelTypeInfo == actionDescriptor.HandlerTypeInfo ? _model : _page;
|
||||
result = await executor(instance, arguments);
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
if (propertyFilter != null)
|
||||
{
|
||||
result = new PageResult(_page);
|
||||
propertyFilter.Subject = _model;
|
||||
propertyFilter.ApplyTempDataChanges(_pageContext.HttpContext);
|
||||
}
|
||||
|
||||
await result.ExecuteResultAsync(_pageContext);
|
||||
_result = await ExecuteHandlerMethod(_model);
|
||||
if (_result is PageResult pageResult)
|
||||
{
|
||||
// If we get here we're going to render the page so we need to initialize the context.
|
||||
pageResult.Page = _page;
|
||||
pageResult.ViewData = pageResult.ViewData ?? _pageContext.ViewData;
|
||||
}
|
||||
|
||||
await _result.ExecuteResultAsync(_pageContext);
|
||||
}
|
||||
|
||||
private async Task<object[]> GetArguments(HandlerMethodDescriptor handler)
|
||||
|
|
@ -409,7 +454,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var parameter = handler.Parameters[i];
|
||||
|
||||
var result = await _parameterBinder.BindModelAsync(
|
||||
_page.PageContext,
|
||||
_pageContext,
|
||||
valueProvider,
|
||||
parameter,
|
||||
value: null);
|
||||
|
|
@ -431,6 +476,36 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
return arguments;
|
||||
}
|
||||
|
||||
private async Task<IActionResult> ExecuteHandlerMethod(object instance)
|
||||
{
|
||||
IActionResult result = null;
|
||||
|
||||
var handler = _selector.Select(_pageContext);
|
||||
if (handler != null)
|
||||
{
|
||||
var arguments = await GetArguments(handler);
|
||||
|
||||
Func<object, object[], Task<IActionResult>> executor = null;
|
||||
for (var i = 0; i < _actionDescriptor.HandlerMethods.Count; i++)
|
||||
{
|
||||
if (object.ReferenceEquals(handler, _actionDescriptor.HandlerMethods[i]))
|
||||
{
|
||||
executor = CacheEntry.Executors[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result = await executor(instance, arguments);
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
result = new PageResult();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task InvokeNextExceptionFilterAsync()
|
||||
{
|
||||
try
|
||||
|
|
|
|||
|
|
@ -5,7 +5,10 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
|
|
@ -13,16 +16,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
public PageActionInvokerCacheEntry(
|
||||
CompiledPageActionDescriptor actionDescriptor,
|
||||
Func<PageContext, object> pageFactory,
|
||||
Action<PageContext, object> releasePage,
|
||||
Func<IModelMetadataProvider, ModelStateDictionary, ViewDataDictionary> viewDataFactory,
|
||||
Func<PageContext, ViewContext, object> pageFactory,
|
||||
Action<PageContext, ViewContext, object> releasePage,
|
||||
Func<PageContext, object> modelFactory,
|
||||
Action<PageContext, object> releaseModel,
|
||||
Func<Page, object, Task> propertyBinder,
|
||||
Func<PageContext, object, Task> propertyBinder,
|
||||
Func<object, object[], Task<IActionResult>>[] executors,
|
||||
IReadOnlyList<Func<IRazorPage>> viewStartFactories,
|
||||
FilterItem[] cacheableFilters)
|
||||
{
|
||||
ActionDescriptor = actionDescriptor;
|
||||
ViewDataFactory = viewDataFactory;
|
||||
PageFactory = pageFactory;
|
||||
ReleasePage = releasePage;
|
||||
ModelFactory = modelFactory;
|
||||
|
|
@ -35,12 +40,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
public CompiledPageActionDescriptor ActionDescriptor { get; }
|
||||
|
||||
public Func<PageContext, object> PageFactory { get; }
|
||||
public Func<PageContext, ViewContext, object> PageFactory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The action invoked to release a page. This may be <c>null</c>.
|
||||
/// </summary>
|
||||
public Action<PageContext, object> ReleasePage { get; }
|
||||
public Action<PageContext, ViewContext, object> ReleasePage { get; }
|
||||
|
||||
public Func<PageContext, object> ModelFactory { get; }
|
||||
|
||||
|
|
@ -53,10 +58,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
/// The delegate invoked to bind either the handler type (page or model).
|
||||
/// This may be <c>null</c>.
|
||||
/// </summary>
|
||||
public Func<Page, object, Task> PropertyBinder { get; }
|
||||
public Func<PageContext, object, Task> PropertyBinder { get; }
|
||||
|
||||
public Func<object, object[], Task<IActionResult>>[] Executors { get; }
|
||||
|
||||
public Func<IModelMetadataProvider, ModelStateDictionary, ViewDataDictionary> ViewDataFactory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the applicable ViewStart pages.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||
{
|
||||
|
|
@ -146,14 +146,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
PageActionInvokerCacheEntry cacheEntry,
|
||||
IFilterMetadata[] filters)
|
||||
{
|
||||
var tempData = _tempDataFactory.GetTempData(actionContext.HttpContext);
|
||||
var pageContext = new PageContext(
|
||||
actionContext,
|
||||
new ViewDataDictionary(_modelMetadataProvider, actionContext.ModelState),
|
||||
tempData,
|
||||
_htmlHelperOptions);
|
||||
|
||||
pageContext.ActionDescriptor = cacheEntry.ActionDescriptor;
|
||||
var pageContext = new PageContext(actionContext)
|
||||
{
|
||||
ActionDescriptor = cacheEntry.ActionDescriptor,
|
||||
ViewData = cacheEntry.ViewDataFactory(_modelMetadataProvider, actionContext.ModelState),
|
||||
ViewStartFactories = cacheEntry.ViewStartFactories.ToList(),
|
||||
};
|
||||
|
||||
return new PageActionInvoker(
|
||||
_selector,
|
||||
|
|
@ -163,7 +161,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
filters,
|
||||
new CopyOnWriteList<IValueProviderFactory>(_valueProviderFactories),
|
||||
cacheEntry,
|
||||
_parameterBinder);
|
||||
_parameterBinder,
|
||||
_tempDataFactory,
|
||||
_htmlHelperOptions);
|
||||
}
|
||||
|
||||
private PageActionInvokerCacheEntry CreateCacheEntry(
|
||||
|
|
@ -173,6 +173,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var actionDescriptor = (PageActionDescriptor)context.ActionContext.ActionDescriptor;
|
||||
var compiledActionDescriptor = _loader.Load(actionDescriptor);
|
||||
|
||||
var viewDataFactory = ViewDataDictionaryFactory.CreateFactory(compiledActionDescriptor.ModelTypeInfo);
|
||||
|
||||
var pageFactory = _pageFactoryProvider.CreatePageFactory(compiledActionDescriptor);
|
||||
var pageDisposer = _pageFactoryProvider.CreatePageDisposer(compiledActionDescriptor);
|
||||
var propertyBinder = PagePropertyBinderFactory.CreateBinder(
|
||||
|
|
@ -194,6 +196,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
return new PageActionInvokerCacheEntry(
|
||||
compiledActionDescriptor,
|
||||
viewDataFactory,
|
||||
pageFactory,
|
||||
pageDisposer,
|
||||
modelFactory,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
public static class PagePropertyBinderFactory
|
||||
{
|
||||
public static Func<Page, object, Task> CreateBinder(
|
||||
public static Func<PageContext, object, Task> CreateBinder(
|
||||
ParameterBinder parameterBinder,
|
||||
IModelMetadataProvider modelMetadataProvider,
|
||||
CompiledPageActionDescriptor actionDescriptor)
|
||||
|
|
@ -45,20 +45,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
return Bind;
|
||||
|
||||
Task Bind(Page page, object model)
|
||||
Task Bind(PageContext pageContext, object instance)
|
||||
{
|
||||
if (page == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(page));
|
||||
}
|
||||
|
||||
if (!isHandlerThePage && model == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(model));
|
||||
}
|
||||
|
||||
var pageContext = page.PageContext;
|
||||
var instance = isHandlerThePage ? page : model;
|
||||
return BindPropertiesAsync(parameterBinder, pageContext, instance, properties, metadata);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,12 +22,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var razorView = (RazorView)context.View;
|
||||
if (ReferenceEquals(page, razorView.RazorPage))
|
||||
{
|
||||
var pageContext = (PageContext)context;
|
||||
var actionDescriptor = (CompiledPageActionDescriptor)context.ActionDescriptor;
|
||||
var vddType = typeof(ViewDataDictionary<>);
|
||||
|
||||
var modelTypeInfo = pageContext.ActionDescriptor.ModelTypeInfo ?? pageContext.ActionDescriptor.PageTypeInfo;
|
||||
|
||||
|
||||
var modelTypeInfo = actionDescriptor.ModelTypeInfo ?? actionDescriptor.PageTypeInfo;
|
||||
vddType = vddType.MakeGenericType(modelTypeInfo.AsType());
|
||||
|
||||
context.ViewData = (ViewDataDictionary)Activator.CreateInstance(vddType, context.ViewData);
|
||||
|
|
|
|||
|
|
@ -37,14 +37,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
public PageContext PageContext { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override ViewContext ViewContext
|
||||
{
|
||||
get => PageContext;
|
||||
set
|
||||
{
|
||||
PageContext = (PageContext)value;
|
||||
}
|
||||
}
|
||||
public override ViewContext ViewContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Http.HttpContext"/>.
|
||||
|
|
@ -502,7 +495,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
/// Returning a <see cref="PageResult"/> from a page handler method is equivalent to returning void.
|
||||
/// The view associated with the page will be executed.
|
||||
/// </remarks>
|
||||
public virtual PageResult Page() => new PageResult(this);
|
||||
public virtual PageResult Page() => new PageResult();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a <see cref="RedirectResult"/> object that redirects to the specified <paramref name="url"/>.
|
||||
|
|
|
|||
|
|
@ -2,25 +2,22 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||
{
|
||||
/// <summary>
|
||||
/// The context associated with the current request for a Razor page.
|
||||
/// </summary>
|
||||
public class PageContext : ViewContext
|
||||
public class PageContext : ActionContext
|
||||
{
|
||||
private CompiledPageActionDescriptor _actionDescriptor;
|
||||
private Page _page;
|
||||
private IList<IValueProviderFactory> _valueProviderFactories;
|
||||
private ViewDataDictionary _viewData;
|
||||
private IList<Func<IRazorPage>> _viewStartFactories;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an empty <see cref="PageContext"/>.
|
||||
|
|
@ -36,53 +33,32 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
/// Initializes a new instance of <see cref="PageContext"/>.
|
||||
/// </summary>
|
||||
/// <param name="actionContext">The <see cref="ActionContext"/>.</param>
|
||||
/// <param name="viewData">The <see cref="ViewDataDictionary"/>.</param>
|
||||
/// <param name="tempDataDictionary">The <see cref="ITempDataDictionary"/>.</param>
|
||||
/// <param name="htmlHelperOptions">The <see cref="HtmlHelperOptions"/> to apply to this instance.</param>
|
||||
public PageContext(
|
||||
ActionContext actionContext,
|
||||
ViewDataDictionary viewData,
|
||||
ITempDataDictionary tempDataDictionary,
|
||||
HtmlHelperOptions htmlHelperOptions)
|
||||
: base(actionContext, NullView.Instance, viewData, tempDataDictionary, TextWriter.Null, htmlHelperOptions)
|
||||
public PageContext(ActionContext actionContext)
|
||||
: base(actionContext)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="PageActionDescriptor"/>.
|
||||
/// </summary>
|
||||
public new CompiledPageActionDescriptor ActionDescriptor
|
||||
public virtual new CompiledPageActionDescriptor ActionDescriptor
|
||||
{
|
||||
get
|
||||
{
|
||||
return _actionDescriptor;
|
||||
}
|
||||
set
|
||||
{
|
||||
_actionDescriptor = value;
|
||||
base.ActionDescriptor = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Page Page
|
||||
{
|
||||
get { return _page; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
_page = value;
|
||||
_actionDescriptor = value;
|
||||
base.ActionDescriptor = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the applicable _ViewStart instances.
|
||||
/// </summary>
|
||||
public IReadOnlyList<IRazorPage> ViewStarts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list of <see cref="IValueProviderFactory"/> instances for the current request.
|
||||
/// </summary>
|
||||
|
|
@ -107,5 +83,45 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
_valueProviderFactories = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets <see cref="ViewDataDictionary"/>.
|
||||
/// </summary>
|
||||
public virtual ViewDataDictionary ViewData
|
||||
{
|
||||
get
|
||||
{
|
||||
return _viewData;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
_viewData = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the applicable _ViewStart instances.
|
||||
/// </summary>
|
||||
public virtual IList<Func<IRazorPage>> ViewStartFactories
|
||||
{
|
||||
get
|
||||
{
|
||||
return _viewStartFactories;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
_viewStartFactories = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,6 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|||
using Microsoft.AspNetCore.Mvc.ModelBinding.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.Routing;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
|
|
@ -25,48 +24,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
[PagesBaseClass]
|
||||
public abstract class PageModel
|
||||
{
|
||||
private IObjectModelValidator _objectValidator;
|
||||
private IModelMetadataProvider _metadataProvider;
|
||||
private IModelBinderFactory _modelBinderFactory;
|
||||
private IObjectModelValidator _objectValidator;
|
||||
private ITempDataDictionary _tempData;
|
||||
private IUrlHelper _urlHelper;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IUrlHelper"/>.
|
||||
/// </summary>
|
||||
public IUrlHelper Url
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_urlHelper == null)
|
||||
{
|
||||
var factory = HttpContext?.RequestServices?.GetRequiredService<IUrlHelperFactory>();
|
||||
_urlHelper = factory?.GetUrlHelper(PageContext);
|
||||
}
|
||||
|
||||
return _urlHelper;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
_urlHelper = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="RazorPages.PageContext"/>.
|
||||
/// </summary>
|
||||
[PageContext]
|
||||
public PageContext PageContext { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ViewContext"/>.
|
||||
/// </summary>
|
||||
public ViewContext ViewContext => PageContext;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Http.HttpContext"/>.
|
||||
/// </summary>
|
||||
|
|
@ -98,10 +67,61 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
public ClaimsPrincipal User => HttpContext?.User;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ITempDataDictionary"/> from the <see cref="PageContext"/>.
|
||||
/// Gets or sets <see cref="ITempDataDictionary"/> used by <see cref="PageResult"/>.
|
||||
/// </summary>
|
||||
/// <remarks>Returns null if <see cref="PageContext"/> is null.</remarks>
|
||||
public ITempDataDictionary TempData => PageContext?.TempData;
|
||||
public ITempDataDictionary TempData
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_tempData == null)
|
||||
{
|
||||
var factory = HttpContext?.RequestServices?.GetRequiredService<ITempDataDictionaryFactory>();
|
||||
_tempData = factory?.GetTempData(HttpContext);
|
||||
}
|
||||
|
||||
return _tempData;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
_tempData = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="IUrlHelper"/>.
|
||||
/// </summary>
|
||||
public IUrlHelper Url
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_urlHelper == null)
|
||||
{
|
||||
var factory = HttpContext?.RequestServices?.GetRequiredService<IUrlHelperFactory>();
|
||||
_urlHelper = factory?.GetUrlHelper(PageContext);
|
||||
}
|
||||
|
||||
return _urlHelper;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
_urlHelper = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets <see cref="ViewDataDictionary"/> used by <see cref="PageResult"/>.
|
||||
/// </summary>
|
||||
public ViewDataDictionary ViewData => PageContext?.ViewData;
|
||||
|
||||
private IObjectModelValidator ObjectValidator
|
||||
{
|
||||
|
|
@ -142,11 +162,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ViewDataDictionary"/>.
|
||||
/// </summary>
|
||||
public ViewDataDictionary ViewData => PageContext?.ViewData;
|
||||
|
||||
/// <summary>
|
||||
/// Updates the specified <paramref name="model"/> instance using values from the <see cref="PageModel"/>'s current
|
||||
/// <see cref="IValueProvider"/>.
|
||||
|
|
@ -797,7 +812,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
/// Creates a <see cref="PageResult"/> object that renders the page.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="PageResult"/>.</returns>
|
||||
public virtual PageResult Page() => new PageResult(PageContext.Page, this);
|
||||
public virtual PageResult Page() => new PageResult();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the file specified by <paramref name="physicalPath" /> (<see cref="StatusCodes.Status200OK"/>) with the
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages
|
||||
|
|
@ -13,26 +14,6 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
/// </summary>
|
||||
public class PageResult : ActionResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="PageResult"/>.
|
||||
/// </summary>
|
||||
/// <param name="page">The <see cref="RazorPages.PageBase"/> to render.</param>
|
||||
public PageResult(PageBase page)
|
||||
{
|
||||
Page = page;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="PageResult"/> with the specified <paramref name="model"/>.
|
||||
/// </summary>
|
||||
/// <param name="page">The <see cref="RazorPages.PageBase"/> to render.</param>
|
||||
/// <param name="model">The page model.</param>
|
||||
public PageResult(PageBase page, object model)
|
||||
{
|
||||
Page = page;
|
||||
Model = model;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Content-Type header for the response.
|
||||
/// </summary>
|
||||
|
|
@ -41,12 +22,17 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
/// <summary>
|
||||
/// Gets the page model.
|
||||
/// </summary>
|
||||
public object Model { get; }
|
||||
public object Model => ViewData?.Model;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="RazorPages.Page"/> to execute.
|
||||
/// Gets or sets the <see cref="PageBase"/> to be executed.
|
||||
/// </summary>
|
||||
public PageBase Page { get; }
|
||||
public PageBase Page { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="ViewDataDictionary"/> for the page to be executed.
|
||||
/// </summary>
|
||||
public ViewDataDictionary ViewData { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the HTTP status code.
|
||||
|
|
@ -56,14 +42,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
/// <inheritdoc />
|
||||
public override Task ExecuteResultAsync(ActionContext context)
|
||||
{
|
||||
if (!object.ReferenceEquals(context, Page.PageContext))
|
||||
if (!(context is PageContext pageContext))
|
||||
{
|
||||
throw new ArgumentException(
|
||||
Resources.FormatPageViewResult_ContextIsInvalid(nameof(context), nameof(Page)));
|
||||
throw new ArgumentException(Resources.FormatPageViewResult_ContextIsInvalid(
|
||||
nameof(context),
|
||||
nameof(Page),
|
||||
nameof(PageResult)));
|
||||
}
|
||||
|
||||
var executor = context.HttpContext.RequestServices.GetRequiredService<PageResultExecutor>();
|
||||
return executor.ExecuteAsync(Page.PageContext, this);
|
||||
return executor.ExecuteAsync(pageContext, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
=> string.Format(CultureInfo.CurrentCulture, GetString("ActivatedInstance_MustBeAnInstanceOf"), p0, p1);
|
||||
|
||||
/// <summary>
|
||||
/// Argument '{0}' is not the same instance used to create '{1}'.
|
||||
/// The context used to execute '{0}' must be an instance of '{1}'. Returning a '{2}' from a controller is a not supported.
|
||||
/// </summary>
|
||||
internal static string PageViewResult_ContextIsInvalid
|
||||
{
|
||||
|
|
@ -61,10 +61,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Argument '{0}' is not the same instance used to create '{1}'.
|
||||
/// The context used to execute '{0}' must be an instance of '{1}'. Returning a '{2}' from a controller is a not supported.
|
||||
/// </summary>
|
||||
internal static string FormatPageViewResult_ContextIsInvalid(object p0, object p1)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("PageViewResult_ContextIsInvalid"), p0, p1);
|
||||
internal static string FormatPageViewResult_ContextIsInvalid(object p0, object p1, object p2)
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("PageViewResult_ContextIsInvalid"), p0, p1, p2);
|
||||
|
||||
/// <summary>
|
||||
/// Value cannot be null or empty.
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@
|
|||
<value>Page created by '{0}' must be an instance of '{1}'.</value>
|
||||
</data>
|
||||
<data name="PageViewResult_ContextIsInvalid" xml:space="preserve">
|
||||
<value>Argument '{0}' is not the same instance used to create '{1}'.</value>
|
||||
<value>The context used to execute '{0}' must be an instance of '{1}'. Returning a '{2}' from a controller is a not supported.</value>
|
||||
</data>
|
||||
<data name="ArgumentCannotBeNullOrEmpty" xml:space="preserve">
|
||||
<value>Value cannot be null or empty.</value>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
// 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.Diagnostics;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
||||
{
|
||||
public static class ViewDataDictionaryFactory
|
||||
{
|
||||
public static Func<IModelMetadataProvider, ModelStateDictionary, ViewDataDictionary> CreateFactory(TypeInfo modelType)
|
||||
{
|
||||
if (modelType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modelType));
|
||||
}
|
||||
|
||||
var type = typeof(ViewDataDictionary<>).MakeGenericType(modelType);
|
||||
var constructor = type.GetConstructor(new Type[] { typeof(IModelMetadataProvider), typeof(ModelStateDictionary) });
|
||||
Debug.Assert(constructor != null);
|
||||
|
||||
var parameter1 = Expression.Parameter(typeof(IModelMetadataProvider), "metadataProvider");
|
||||
var parameter2 = Expression.Parameter(typeof(ModelStateDictionary), "modelState");
|
||||
|
||||
return
|
||||
Expression.Lambda<Func<IModelMetadataProvider, ModelStateDictionary, ViewDataDictionary>>(
|
||||
Expression.Convert(
|
||||
Expression.New(constructor, parameter1, parameter2),
|
||||
typeof(ViewDataDictionary)),
|
||||
parameter1,
|
||||
parameter2)
|
||||
.Compile();
|
||||
}
|
||||
|
||||
public static Func<ViewDataDictionary, ViewDataDictionary> CreateNestedFactory(TypeInfo modelType)
|
||||
{
|
||||
if (modelType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modelType));
|
||||
}
|
||||
|
||||
var type = typeof(ViewDataDictionary<>).MakeGenericType(modelType);
|
||||
var constructor = type.GetConstructor(new Type[] { typeof(ViewDataDictionary) });
|
||||
Debug.Assert(constructor != null);
|
||||
|
||||
var parameter = Expression.Parameter(typeof(ViewDataDictionary), "viewDataDictionary");
|
||||
|
||||
return
|
||||
Expression.Lambda<Func<ViewDataDictionary, ViewDataDictionary>>(
|
||||
Expression.Convert(
|
||||
Expression.New(constructor, parameter),
|
||||
typeof(ViewDataDictionary)),
|
||||
parameter)
|
||||
.Compile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,20 +4,21 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||
{
|
||||
public class DefaultPageActivatorTest
|
||||
public class DefaultPageActivatorProviderTest
|
||||
{
|
||||
[Fact]
|
||||
public void CreateActivator_ThrowsIfPageTypeInfoIsNull()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new CompiledPageActionDescriptor();
|
||||
var activator = new DefaultPageActivator();
|
||||
var activator = new DefaultPageActivatorProvider();
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.ThrowsArgument(
|
||||
|
|
@ -33,16 +34,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
{
|
||||
// Arrange
|
||||
var pageContext = new PageContext();
|
||||
var viewContext = new ViewContext();
|
||||
var descriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = type.GetTypeInfo(),
|
||||
};
|
||||
|
||||
var activator = new DefaultPageActivator();
|
||||
|
||||
var activator = new DefaultPageActivatorProvider();
|
||||
|
||||
// Act
|
||||
var factory = activator.CreateActivator(descriptor);
|
||||
var instance = factory(pageContext);
|
||||
var instance = factory(pageContext, viewContext);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(instance);
|
||||
|
|
@ -58,7 +61,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
PageTypeInfo = typeof(PageWithoutParameterlessConstructor).GetTypeInfo(),
|
||||
};
|
||||
var pageContext = new PageContext();
|
||||
var activator = new DefaultPageActivator();
|
||||
var activator = new DefaultPageActivatorProvider();
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentException>(() => activator.CreateActivator(descriptor));
|
||||
|
|
@ -71,7 +74,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
{
|
||||
// Arrange
|
||||
var context = new PageContext();
|
||||
var activator = new DefaultPageActivator();
|
||||
var activator = new DefaultPageActivatorProvider();
|
||||
var page = new TestPage();
|
||||
|
||||
// Act
|
||||
|
|
@ -89,7 +92,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
{
|
||||
// Arrange
|
||||
var context = new PageContext();
|
||||
var activator = new DefaultPageActivator();
|
||||
var viewContext = new ViewContext();
|
||||
var activator = new DefaultPageActivatorProvider();
|
||||
var page = new DisposablePage();
|
||||
|
||||
// Act & Assert
|
||||
|
|
@ -98,7 +102,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
PageTypeInfo = page.GetType().GetTypeInfo()
|
||||
});
|
||||
Assert.NotNull(disposer);
|
||||
disposer(context, page);
|
||||
disposer(context, viewContext, page);
|
||||
|
||||
// Assert
|
||||
Assert.True(page.Disposed);
|
||||
|
|
@ -53,11 +53,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
{
|
||||
ActionDescriptor = descriptor
|
||||
};
|
||||
var viewContext = new ViewContext();
|
||||
var factoryProvider = CreatePageFactory();
|
||||
|
||||
// Act
|
||||
var factory = factoryProvider.CreatePageFactory(descriptor);
|
||||
var instance = factory(pageContext);
|
||||
var instance = factory(pageContext, viewContext);
|
||||
|
||||
// Assert
|
||||
var testPage = Assert.IsType<TestPage>(instance);
|
||||
|
|
@ -75,11 +76,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
}
|
||||
};
|
||||
|
||||
var viewContext = new ViewContext();
|
||||
|
||||
var urlHelperFactory = new Mock<IUrlHelperFactory>();
|
||||
var urlHelper = Mock.Of<IUrlHelper>();
|
||||
urlHelperFactory.Setup(f => f.GetUrlHelper(pageContext))
|
||||
urlHelperFactory
|
||||
.Setup(f => f.GetUrlHelper(viewContext))
|
||||
.Returns(urlHelper)
|
||||
.Verifiable();
|
||||
|
||||
var htmlEncoder = HtmlEncoder.Create();
|
||||
|
||||
var factoryProvider = CreatePageFactory(
|
||||
|
|
@ -88,7 +94,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
|
||||
// Act
|
||||
var factory = factoryProvider.CreatePageFactory(pageContext.ActionDescriptor);
|
||||
var instance = factory(pageContext);
|
||||
var instance = factory(pageContext, viewContext);
|
||||
|
||||
// Assert
|
||||
var testPage = Assert.IsType<TestPage>(instance);
|
||||
|
|
@ -113,9 +119,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
ActionDescriptor = descriptor
|
||||
};
|
||||
|
||||
var viewContext = new ViewContext();
|
||||
|
||||
// Act
|
||||
var factory = CreatePageFactory().CreatePageFactory(descriptor);
|
||||
var instance = factory(pageContext);
|
||||
var instance = factory(pageContext, viewContext);
|
||||
|
||||
// Assert
|
||||
var testPage = Assert.IsType<ViewDataTestPage>(instance);
|
||||
|
|
@ -135,11 +143,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
},
|
||||
};
|
||||
|
||||
var viewContext = new ViewContext();
|
||||
|
||||
var factoryProvider = CreatePageFactory();
|
||||
|
||||
// Act
|
||||
var factory = factoryProvider.CreatePageFactory(pageContext.ActionDescriptor);
|
||||
var instance = factory(pageContext);
|
||||
var instance = factory(pageContext, viewContext);
|
||||
|
||||
// Assert
|
||||
var testPage = Assert.IsType<ViewDataTestPage>(instance);
|
||||
|
|
@ -158,11 +168,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
},
|
||||
};
|
||||
|
||||
var viewContext = new ViewContext();
|
||||
|
||||
var factoryProvider = CreatePageFactory();
|
||||
|
||||
// Act
|
||||
var factory = factoryProvider.CreatePageFactory(pageContext.ActionDescriptor);
|
||||
var instance = factory(pageContext);
|
||||
var instance = factory(pageContext, viewContext);
|
||||
|
||||
// Assert
|
||||
var testPage = Assert.IsType<NonGenericViewDataTestPage>(instance);
|
||||
|
|
@ -170,7 +182,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void PageFactorySetsNestedVidewDataDictionaryWhenContextHasANonNullDictionary()
|
||||
public void PageFactory_SetsViewDataOnPage_FromPageContext()
|
||||
{
|
||||
// Arrange
|
||||
var modelMetadataProvider = new EmptyModelMetadataProvider();
|
||||
|
|
@ -180,21 +192,28 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
{
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo()
|
||||
},
|
||||
ViewData = new ViewDataDictionary(modelMetadataProvider, new ModelStateDictionary())
|
||||
ViewData = new ViewDataDictionary<TestPage>(modelMetadataProvider, new ModelStateDictionary())
|
||||
{
|
||||
{ "test-key", "test-value" },
|
||||
}
|
||||
};
|
||||
|
||||
var viewContext = new ViewContext()
|
||||
{
|
||||
HttpContext = pageContext.HttpContext,
|
||||
ViewData = pageContext.ViewData,
|
||||
};
|
||||
|
||||
var factoryProvider = CreatePageFactory();
|
||||
|
||||
// Act
|
||||
var factory = factoryProvider.CreatePageFactory(pageContext.ActionDescriptor);
|
||||
var instance = factory(pageContext);
|
||||
var instance = factory(pageContext, viewContext);
|
||||
|
||||
// Assert
|
||||
var testPage = Assert.IsType<TestPage>(instance);
|
||||
Assert.NotNull(testPage.ViewData);
|
||||
Assert.Same(pageContext.ViewData, testPage.ViewData);
|
||||
Assert.Equal("test-value", testPage.ViewData["test-key"]);
|
||||
}
|
||||
|
||||
|
|
@ -205,6 +224,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
var serviceProvider = new ServiceCollection()
|
||||
.AddSingleton<ILogger>(NullLogger.Instance)
|
||||
.BuildServiceProvider();
|
||||
|
||||
var pageContext = new PageContext
|
||||
{
|
||||
ActionDescriptor = new CompiledPageActionDescriptor
|
||||
|
|
@ -215,14 +235,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
{
|
||||
RequestServices = serviceProvider,
|
||||
},
|
||||
};
|
||||
|
||||
var viewContext = new ViewContext()
|
||||
{
|
||||
HttpContext = pageContext.HttpContext,
|
||||
};
|
||||
|
||||
var factoryProvider = CreatePageFactory();
|
||||
|
||||
// Act
|
||||
var factory = factoryProvider.CreatePageFactory(pageContext.ActionDescriptor);
|
||||
var instance = factory(pageContext);
|
||||
var instance = factory(pageContext, viewContext);
|
||||
|
||||
// Assert
|
||||
var testPage = Assert.IsType<PropertiesWithoutRazorInject>(instance);
|
||||
|
|
@ -258,10 +282,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
private static IPageActivatorProvider CreateActivator()
|
||||
{
|
||||
var activator = new Mock<IPageActivatorProvider>();
|
||||
activator.Setup(a => a.CreateActivator(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
activator
|
||||
.Setup(a => a.CreateActivator(It.IsAny<CompiledPageActionDescriptor>()))
|
||||
.Returns((CompiledPageActionDescriptor descriptor) =>
|
||||
{
|
||||
return (context) => Activator.CreateInstance(descriptor.PageTypeInfo.AsType());
|
||||
return (context, viewContext) => Activator.CreateInstance(descriptor.PageTypeInfo.AsType());
|
||||
});
|
||||
return activator.Object;
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
|
@ -95,7 +96,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
{
|
||||
// Arrange
|
||||
var context = new PageContext();
|
||||
var activator = new DefaultPageActivator();
|
||||
var activator = new DefaultPageModelActivatorProvider();
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = pageType.GetTypeInfo(),
|
||||
|
|
@ -113,11 +114,13 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
|||
{
|
||||
// Arrange
|
||||
var context = new PageContext();
|
||||
var activator = new DefaultPageActivator();
|
||||
|
||||
var activator = new DefaultPageModelActivatorProvider();
|
||||
var actionDescriptor = new CompiledPageActionDescriptor
|
||||
{
|
||||
PageTypeInfo = typeof(DisposableModel).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(DisposableModel).GetTypeInfo(),
|
||||
};
|
||||
|
||||
var model = new DisposableModel();
|
||||
|
||||
// Act & Assert
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
|||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Internal;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
|
@ -38,8 +39,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
FilterDescriptors = new FilterDescriptor[0],
|
||||
};
|
||||
|
||||
Func<PageContext, object> factory = _ => null;
|
||||
Action<PageContext, object> releaser = (_, __) => { };
|
||||
Func<PageContext, ViewContext, object> factory = (a, b) => null;
|
||||
Action<PageContext, ViewContext, object> releaser = (a, b, c) => { };
|
||||
|
||||
var loader = new Mock<IPageLoader>();
|
||||
loader
|
||||
|
|
@ -78,6 +79,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Assert.Same(releaser, entry.ReleasePage);
|
||||
Assert.Null(entry.ModelFactory);
|
||||
Assert.Null(entry.ReleaseModel);
|
||||
Assert.NotNull(entry.ViewDataFactory);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -90,8 +92,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
FilterDescriptors = new FilterDescriptor[0],
|
||||
};
|
||||
|
||||
Func<PageContext, object> factory = _ => null;
|
||||
Action<PageContext, object> releaser = (_, __) => { };
|
||||
Func<PageContext, ViewContext, object> factory = (a, b) => null;
|
||||
Action<PageContext, ViewContext, object> releaser = (a, b, c) => { };
|
||||
Func<PageContext, object> modelFactory = _ => null;
|
||||
Action<PageContext, object> modelDisposer = (_, __) => { };
|
||||
|
||||
|
|
@ -134,7 +136,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
// Assert
|
||||
Assert.NotNull(context.Result);
|
||||
|
||||
var actionInvoker = Assert.IsType<PageActionInvoker>(context.Result);
|
||||
|
||||
var entry = actionInvoker.CacheEntry;
|
||||
var compiledPageActionDescriptor = Assert.IsType<CompiledPageActionDescriptor>(entry.ActionDescriptor);
|
||||
Assert.Equal(descriptor.RelativePath, compiledPageActionDescriptor.RelativePath);
|
||||
|
|
@ -142,6 +146,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
Assert.Same(releaser, entry.ReleasePage);
|
||||
Assert.Same(modelFactory, entry.ModelFactory);
|
||||
Assert.Same(modelDisposer, entry.ReleaseModel);
|
||||
Assert.NotNull(entry.ViewDataFactory);
|
||||
|
||||
var pageContext = actionInvoker.PageContext;
|
||||
Assert.Same(compiledPageActionDescriptor, pageContext.ActionDescriptor);
|
||||
Assert.Same(context.ActionContext.HttpContext, pageContext.HttpContext);
|
||||
Assert.Same(context.ActionContext.ModelState, pageContext.ModelState);
|
||||
Assert.Same(context.ActionContext.RouteData, pageContext.RouteData);
|
||||
Assert.Empty(pageContext.ValueProviderFactories);
|
||||
Assert.NotNull(Assert.IsType<ViewDataDictionary<TestPageModel>>(pageContext.ViewData));
|
||||
Assert.Empty(pageContext.ViewStartFactories);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -476,16 +490,20 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
PageActionDescriptor descriptor,
|
||||
Type pageType = null)
|
||||
{
|
||||
pageType = pageType ?? typeof(object);
|
||||
var pageTypeInfo = pageType.GetTypeInfo();
|
||||
|
||||
TypeInfo modelTypeInfo = null;
|
||||
if (pageType != null)
|
||||
{
|
||||
modelTypeInfo = pageType.GetTypeInfo().GetProperty("Model")?.PropertyType.GetTypeInfo();
|
||||
modelTypeInfo = pageTypeInfo.GetProperty("Model")?.PropertyType.GetTypeInfo();
|
||||
}
|
||||
|
||||
return new CompiledPageActionDescriptor(descriptor)
|
||||
{
|
||||
ModelTypeInfo = modelTypeInfo,
|
||||
PageTypeInfo = (pageType ?? typeof(object)).GetTypeInfo()
|
||||
HandlerTypeInfo = modelTypeInfo ?? pageTypeInfo,
|
||||
ModelTypeInfo = modelTypeInfo ?? pageTypeInfo,
|
||||
PageTypeInfo = pageTypeInfo,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,10 @@ using Microsoft.AspNetCore.Mvc.ModelBinding;
|
|||
using Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
|
||||
using Microsoft.AspNetCore.Mvc.Razor;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewEngines;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
|
@ -344,10 +346,18 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
.Verifiable();
|
||||
|
||||
var filter3 = new Mock<IAuthorizationFilter>(MockBehavior.Strict);
|
||||
var actionDescriptor = new CompiledPageActionDescriptor();
|
||||
|
||||
var actionDescriptor = new CompiledPageActionDescriptor()
|
||||
{
|
||||
HandlerTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
};
|
||||
|
||||
var cacheEntry = new PageActionInvokerCacheEntry(
|
||||
actionDescriptor,
|
||||
(context) => createCalled = true,
|
||||
null,
|
||||
(context, viewContext) => createCalled = true,
|
||||
null,
|
||||
(context) => null,
|
||||
null,
|
||||
|
|
@ -400,10 +410,17 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
var filter3 = new Mock<IAuthorizationFilter>(MockBehavior.Strict);
|
||||
|
||||
var actionDescriptor = new CompiledPageActionDescriptor();
|
||||
var actionDescriptor = new CompiledPageActionDescriptor()
|
||||
{
|
||||
HandlerTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
ModelTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
PageTypeInfo = typeof(TestPage).GetTypeInfo(),
|
||||
};
|
||||
|
||||
var cacheEntry = new PageActionInvokerCacheEntry(
|
||||
actionDescriptor,
|
||||
(context) => createCalled = true,
|
||||
null,
|
||||
(context, viewContext) => createCalled = true,
|
||||
null,
|
||||
(context) => null,
|
||||
null,
|
||||
|
|
@ -540,6 +557,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
PageResultExecutor executor = null,
|
||||
IPageHandlerMethodSelector selector = null,
|
||||
PageActionInvokerCacheEntry cacheEntry = null,
|
||||
ITempDataDictionaryFactory tempDataFactory = null,
|
||||
int maxAllowedErrorsInModelState = 200,
|
||||
List<IValueProviderFactory> valueProviderFactories = null,
|
||||
RouteData routeData = null,
|
||||
|
|
@ -572,15 +590,14 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
httpContext: httpContext,
|
||||
routeData: routeData,
|
||||
actionDescriptor: actionDescriptor);
|
||||
var pageContext = new PageContext(
|
||||
actionContext,
|
||||
new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary()),
|
||||
Mock.Of<ITempDataDictionary>(),
|
||||
new HtmlHelperOptions())
|
||||
var pageContext = new PageContext(actionContext)
|
||||
{
|
||||
ActionDescriptor = actionDescriptor
|
||||
ActionDescriptor = actionDescriptor,
|
||||
};
|
||||
|
||||
var viewDataFactory = ViewDataDictionaryFactory.CreateFactory(actionDescriptor.ModelTypeInfo);
|
||||
pageContext.ViewData = viewDataFactory(new EmptyModelMetadataProvider(), pageContext.ModelState);
|
||||
|
||||
if (selector == null)
|
||||
{
|
||||
selector = Mock.Of<IPageHandlerMethodSelector>();
|
||||
|
|
@ -596,7 +613,12 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
logger = NullLogger.Instance;
|
||||
}
|
||||
|
||||
Func<PageContext, object> pageFactory = (context) =>
|
||||
if (tempDataFactory == null)
|
||||
{
|
||||
tempDataFactory = Mock.Of<ITempDataDictionaryFactory>(m => m.GetTempData(It.IsAny<HttpContext>()) == Mock.Of<ITempDataDictionary>());
|
||||
}
|
||||
|
||||
Func<PageContext, ViewContext, object> pageFactory = (context, viewContext) =>
|
||||
{
|
||||
var instance = (Page)Activator.CreateInstance(actionDescriptor.PageTypeInfo.AsType());
|
||||
instance.PageContext = context;
|
||||
|
|
@ -605,8 +627,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
|
||||
cacheEntry = new PageActionInvokerCacheEntry(
|
||||
actionDescriptor,
|
||||
viewDataFactory,
|
||||
pageFactory,
|
||||
(c, page) => { (page as IDisposable)?.Dispose(); },
|
||||
(c, viewContext, page) => { (page as IDisposable)?.Dispose(); },
|
||||
_ => Activator.CreateInstance(actionDescriptor.ModelTypeInfo.AsType()),
|
||||
(c, model) => { (model as IDisposable)?.Dispose(); },
|
||||
null,
|
||||
|
|
@ -622,7 +645,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
filters,
|
||||
valueProviderFactories.AsReadOnly(),
|
||||
cacheEntry,
|
||||
GetParameterBinder());
|
||||
GetParameterBinder(),
|
||||
tempDataFactory,
|
||||
new HtmlHelperOptions());
|
||||
return invoker;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
};
|
||||
|
||||
// Act
|
||||
await factory(page, null);
|
||||
await factory(page.PageContext, page);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(10, page.Id);
|
||||
|
|
@ -262,7 +262,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var model = new PageModelWithProperty();
|
||||
|
||||
// Act
|
||||
await factory(page, model);
|
||||
await factory(page.PageContext, model);
|
||||
|
||||
// Assert
|
||||
// Verify that the page properties were not bound.
|
||||
|
|
@ -314,7 +314,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var defaultValue = model.PropertyWithDefaultValue;
|
||||
|
||||
// Act
|
||||
await factory(page, model);
|
||||
await factory(page.PageContext, model);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(defaultValue, model.PropertyWithDefaultValue);
|
||||
|
|
@ -375,7 +375,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var model = new PageModelWithSupportsGetProperty();
|
||||
|
||||
// Act
|
||||
await factory(page, model);
|
||||
await factory(page.PageContext, model);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("value", model.SupportsGet);
|
||||
|
|
@ -434,7 +434,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var model = new PageModelWithSupportsGetProperty();
|
||||
|
||||
// Act
|
||||
await factory(page, model);
|
||||
await factory(page.PageContext, model);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("value", model.SupportsGet);
|
||||
|
|
|
|||
|
|
@ -1406,15 +1406,16 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
var modelState = new ModelStateDictionary();
|
||||
var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor(), modelState);
|
||||
var modelMetadataProvider = new EmptyModelMetadataProvider();
|
||||
var viewDataDictionary = new ViewDataDictionary(modelMetadataProvider, modelState);
|
||||
var tempData = Mock.Of<ITempDataDictionary>();
|
||||
var pageContext = new PageContext(actionContext, viewDataDictionary, tempData, new HtmlHelperOptions());
|
||||
var viewData = new ViewDataDictionary(modelMetadataProvider, modelState);
|
||||
var pageContext = new PageContext(actionContext)
|
||||
{
|
||||
ViewData = viewData,
|
||||
};
|
||||
|
||||
var page = new TestPage
|
||||
{
|
||||
PageContext = pageContext,
|
||||
};
|
||||
pageContext.Page = page;
|
||||
|
||||
var pageModel = new TestPageModel
|
||||
{
|
||||
|
|
@ -1423,13 +1424,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
|
||||
// Act & Assert
|
||||
Assert.Same(pageContext, pageModel.PageContext);
|
||||
Assert.Same(pageContext, pageModel.ViewContext);
|
||||
Assert.Same(httpContext, pageModel.HttpContext);
|
||||
Assert.Same(httpContext.Request, pageModel.Request);
|
||||
Assert.Same(httpContext.Response, pageModel.Response);
|
||||
Assert.Same(modelState, pageModel.ModelState);
|
||||
Assert.Same(viewDataDictionary, pageModel.ViewData);
|
||||
Assert.Same(tempData, pageModel.TempData);
|
||||
Assert.Same(viewData, pageModel.ViewData);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -1483,10 +1482,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
var page = new TestPage();
|
||||
var pageModel = new TestPageModel
|
||||
{
|
||||
PageContext = new PageContext
|
||||
{
|
||||
Page = page,
|
||||
}
|
||||
PageContext = new PageContext()
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -1494,7 +1490,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
|
||||
// Assert
|
||||
var pageResult = Assert.IsType<PageResult>(result);
|
||||
Assert.Same(page, pageResult.Page);
|
||||
Assert.Null(pageResult.Page); // This is set by the invoker
|
||||
}
|
||||
|
||||
private class ContentPageModel : PageModel
|
||||
|
|
|
|||
|
|
@ -9,8 +9,10 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Formatters;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.TestCommon;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.AspNetCore.Testing;
|
||||
using Moq;
|
||||
|
|
@ -28,17 +30,24 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages
|
|||
var modelState = new ModelStateDictionary();
|
||||
var actionContext = new ActionContext(httpContext, new RouteData(), new PageActionDescriptor(), modelState);
|
||||
var modelMetadataProvider = new EmptyModelMetadataProvider();
|
||||
var viewDataDictionary = new ViewDataDictionary(modelMetadataProvider, modelState);
|
||||
var viewData = new ViewDataDictionary(modelMetadataProvider, modelState);
|
||||
var tempData = Mock.Of<ITempDataDictionary>();
|
||||
var pageContext = new PageContext(actionContext, viewDataDictionary, tempData, new HtmlHelperOptions());
|
||||
|
||||
var pageContext = new PageContext(actionContext)
|
||||
{
|
||||
ViewData = viewData,
|
||||
};
|
||||
var viewContext = new ViewContext(pageContext, NullView.Instance, viewData, tempData, TextWriter.Null, new HtmlHelperOptions());
|
||||
|
||||
var page = new TestPage
|
||||
{
|
||||
PageContext = pageContext,
|
||||
ViewContext = viewContext,
|
||||
};
|
||||
|
||||
// Act & Assert
|
||||
Assert.Same(pageContext, page.ViewContext);
|
||||
Assert.Same(pageContext, page.PageContext);
|
||||
Assert.Same(viewContext, page.ViewContext);
|
||||
Assert.Same(httpContext, page.HttpContext);
|
||||
Assert.Same(httpContext.Request, page.Request);
|
||||
Assert.Same(httpContext.Response, page.Response);
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
|
@ -164,7 +166,8 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|||
var testProp = pageType.GetProperty(nameof(TestPageString.Test));
|
||||
var test2Prop = pageType.GetProperty(nameof(TestPageString.Test2));
|
||||
|
||||
provider.TempDataProperties = new List<TempDataProperty> {
|
||||
provider.TempDataProperties = new List<TempDataProperty>
|
||||
{
|
||||
new TempDataProperty(testProp, testProp.GetValue, testProp.SetValue),
|
||||
new TempDataProperty(test2Prop, test2Prop.GetValue, test2Prop.SetValue)
|
||||
};
|
||||
|
|
@ -177,15 +180,17 @@ namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
|
|||
Assert.Null(page.Test2);
|
||||
}
|
||||
|
||||
private static PageContext CreateViewContext(HttpContext httpContext, ITempDataDictionary tempData)
|
||||
private static ViewContext CreateViewContext(HttpContext httpContext, ITempDataDictionary tempData)
|
||||
{
|
||||
var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
|
||||
var metadataProvider = new EmptyModelMetadataProvider();
|
||||
var viewData = new ViewDataDictionary(metadataProvider, new ModelStateDictionary());
|
||||
var viewContext = new PageContext(
|
||||
var viewContext = new ViewContext(
|
||||
actionContext,
|
||||
NullView.Instance,
|
||||
viewData,
|
||||
tempData,
|
||||
TextWriter.Null,
|
||||
new HtmlHelperOptions());
|
||||
|
||||
return viewContext;
|
||||
|
|
|
|||
Loading…
Reference in New Issue