-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Introducing IPageModelActivatorProvider #5712
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
Fixes #5480
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// 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; | ||
|
||
namespace Microsoft.AspNetCore.Mvc.RazorPages | ||
{ | ||
/// <summary> | ||
/// Provides methods to create a Razor page model. | ||
/// </summary> | ||
public interface IPageModelActivatorProvider | ||
{ | ||
/// <summary> | ||
/// Creates a Razor page model activator. | ||
/// </summary> | ||
/// <param name="descriptor">The <see cref="CompiledPageActionDescriptor"/>.</param> | ||
/// <returns>The delegate used to activate the page model.</returns> | ||
Func<PageContext, object> CreateActivator(CompiledPageActionDescriptor descriptor); | ||
|
||
/// <summary> | ||
/// Releases a Razor page model. | ||
/// </summary> | ||
/// <param name="descriptor">The <see cref="CompiledPageActionDescriptor"/>.</param> | ||
/// <returns>The delegate used to dispose the activated page model.</returns> | ||
Action<PageContext, object> CreateReleaser(CompiledPageActionDescriptor descriptor); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
// 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; | ||
|
||
namespace Microsoft.AspNetCore.Mvc.RazorPages | ||
{ | ||
/// <summary> | ||
/// Provides methods for creation and disposal of Razor page models. | ||
/// </summary> | ||
public interface IPageModelFactoryProvider | ||
{ | ||
/// <summary> | ||
/// Creates a factory for producing models for Razor pages given the specified <see cref="PageContext"/>. | ||
/// </summary> | ||
/// <param name="descriptor">The <see cref="CompiledPageActionDescriptor"/>.</param> | ||
/// <returns>The Razor page model factory.</returns> | ||
Func<PageContext, object> CreateModelFactory(CompiledPageActionDescriptor descriptor); | ||
|
||
/// <summary> | ||
/// Releases a Razor page model. | ||
/// </summary> | ||
/// <param name="descriptor">The <see cref="CompiledPageActionDescriptor"/>.</param> | ||
/// <returns>The delegate used to release the created page model.</returns> | ||
Action<PageContext, object> CreateModelDisposer(CompiledPageActionDescriptor descriptor); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Reflection; | ||
using Microsoft.AspNetCore.Mvc.Internal; | ||
using Microsoft.Extensions.DependencyInjection; | ||
|
||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we have a |
||
{ | ||
/// <summary> | ||
/// <see cref="IPageActivatorProvider"/> that uses type activation to create Pages. | ||
/// </summary> | ||
public class DefaultPageModelActivatorProvider : IPageModelActivatorProvider | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We'll also need the equivalent plumbing for the DI versions of these. Please open an issue for that or do it right away 👍 |
||
{ | ||
private readonly Action<PageContext, object> _disposer = Dispose; | ||
|
||
/// <inheritdoc /> | ||
public virtual Func<PageContext, object> CreateActivator(CompiledPageActionDescriptor actionDescriptor) | ||
{ | ||
if (actionDescriptor == null) | ||
{ | ||
throw new ArgumentNullException(nameof(actionDescriptor)); | ||
} | ||
|
||
var modelTypeInfo = actionDescriptor.ModelTypeInfo?.AsType(); | ||
if (modelTypeInfo == null) | ||
{ | ||
throw new ArgumentException(Resources.FormatPropertyOfTypeCannotBeNull( | ||
nameof(actionDescriptor.ModelTypeInfo), | ||
nameof(actionDescriptor)), | ||
nameof(actionDescriptor)); | ||
} | ||
|
||
var factory = ActivatorUtilities.CreateFactory(modelTypeInfo, Type.EmptyTypes); | ||
return (context) => factory(context.HttpContext.RequestServices, EmptyArray<object>.Instance); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be dope to put this part into a virtual method, then there's less boilerplate if you want to use something other than There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nevermind, my brain is crazy. Ignore this comment as soon as possible |
||
} | ||
|
||
public virtual Action<PageContext, object> CreateReleaser(CompiledPageActionDescriptor actionDescriptor) | ||
{ | ||
if (actionDescriptor == null) | ||
{ | ||
throw new ArgumentNullException(nameof(actionDescriptor)); | ||
} | ||
|
||
if (typeof(IDisposable).GetTypeInfo().IsAssignableFrom(actionDescriptor.ModelTypeInfo)) | ||
{ | ||
return _disposer; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
private static void Dispose(PageContext context, object page) | ||
{ | ||
if (context == null) | ||
{ | ||
throw new ArgumentNullException(nameof(context)); | ||
} | ||
|
||
if (page == null) | ||
{ | ||
throw new ArgumentNullException(nameof(page)); | ||
} | ||
|
||
((IDisposable)page).Dispose(); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Reflection; | ||
using Microsoft.Extensions.Internal; | ||
|
||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal | ||
{ | ||
public class DefaultPageModelFactoryProvider : IPageModelFactoryProvider | ||
{ | ||
private static readonly Func<PropertyInfo, PropertyActivator<PageContext>> _createActivateInfo = | ||
CreateActivateInfo; | ||
private readonly IPageModelActivatorProvider _modelActivator; | ||
|
||
public DefaultPageModelFactoryProvider(IPageModelActivatorProvider modelActivator) | ||
{ | ||
_modelActivator = modelActivator; | ||
} | ||
|
||
public virtual Func<PageContext, object> CreateModelFactory(CompiledPageActionDescriptor descriptor) | ||
{ | ||
if (descriptor == null) | ||
{ | ||
throw new ArgumentNullException(nameof(descriptor)); | ||
} | ||
|
||
if (descriptor.ModelTypeInfo == null) | ||
{ | ||
return null; | ||
} | ||
|
||
var modelActivator = _modelActivator.CreateActivator(descriptor); | ||
var propertyActivator = PropertyActivator<PageContext>.GetPropertiesToActivate( | ||
descriptor.ModelTypeInfo.AsType(), | ||
typeof(PageContextAttribute), | ||
_createActivateInfo, | ||
includeNonPublic: false); | ||
|
||
return pageContext => | ||
{ | ||
var model = modelActivator(pageContext); | ||
for (var i = 0; i < propertyActivator.Length; i++) | ||
{ | ||
propertyActivator[i].Activate(model, pageContext); | ||
} | ||
|
||
return model; | ||
}; | ||
} | ||
|
||
public virtual Action<PageContext, object> CreateModelDisposer(CompiledPageActionDescriptor descriptor) | ||
{ | ||
if (descriptor == null) | ||
{ | ||
throw new ArgumentNullException(nameof(descriptor)); | ||
} | ||
|
||
if (descriptor.ModelTypeInfo == null) | ||
{ | ||
return null; | ||
} | ||
|
||
return _modelActivator.CreateReleaser(descriptor); | ||
} | ||
|
||
private static PropertyActivator<PageContext> CreateActivateInfo(PropertyInfo property) => | ||
new PropertyActivator<PageContext>(property, pageContext => pageContext); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
|
||
namespace Microsoft.AspNetCore.Mvc.RazorPages | ||
{ | ||
/// <summary> | ||
/// Specifies that a Razor Page model property should be set with the current <see cref="PageContext"/> when creating | ||
/// the model instance. The property must have a public set method. | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] | ||
public class PageContextAttribute : Attribute | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this go in |
||
{ | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
P
should be capitalized because it's a proper name #JustEilonThings