Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
Introducing IPageModelActivatorProvider
Browse files Browse the repository at this point in the history
Fixes #5480
  • Loading branch information
pranavkm committed Jan 26, 2017
1 parent cc584ad commit 3e214e2
Show file tree
Hide file tree
Showing 13 changed files with 599 additions and 22 deletions.
@@ -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 Razor Page model.</returns>
Action<PageContext, object> CreateReleaser(CompiledPageActionDescriptor descriptor);
}
}
@@ -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 Razor Page model.</returns>
Action<PageContext, object> CreateModelDisposer(CompiledPageActionDescriptor descriptor);
}
}
Expand Up @@ -5,7 +5,7 @@
using System.Linq.Expressions;
using System.Reflection;

namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
/// <summary>
/// <see cref="IPageActivatorProvider"/> that uses type activation to create Pages.
Expand Down
Expand Up @@ -11,7 +11,7 @@
using Microsoft.AspNetCore.Mvc.Routing;
using Microsoft.AspNetCore.Mvc.ViewFeatures;

namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
public class DefaultPageFactory : IPageFactoryProvider
{
Expand Down
@@ -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.Infrastructure
{
/// <summary>
/// <see cref="IPageActivatorProvider"/> that uses type activation to create Razor Page instances.
/// </summary>
public class DefaultPageModelActivatorProvider : IPageModelActivatorProvider
{
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);
}

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();
}
}
}
@@ -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.Infrastructure
{
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);
}
}
Expand Up @@ -24,6 +24,7 @@ public class PageActionInvokerProvider : IActionInvokerProvider
private const string ModelPropertyName = "Model";
private readonly IPageLoader _loader;
private readonly IPageFactoryProvider _pageFactoryProvider;
private readonly IPageModelFactoryProvider _modelFactoryProvider;
private readonly IActionDescriptorCollectionProvider _collectionProvider;
private readonly IFilterProvider[] _filterProviders;
private readonly IReadOnlyList<IValueProviderFactory> _valueProviderFactories;
Expand All @@ -38,6 +39,7 @@ public class PageActionInvokerProvider : IActionInvokerProvider
public PageActionInvokerProvider(
IPageLoader loader,
IPageFactoryProvider pageFactoryProvider,
IPageModelFactoryProvider modelFactoryProvider,
IActionDescriptorCollectionProvider collectionProvider,
IEnumerable<IFilterProvider> filterProviders,
IEnumerable<IValueProviderFactory> valueProviderFactories,
Expand All @@ -49,8 +51,9 @@ public class PageActionInvokerProvider : IActionInvokerProvider
ILoggerFactory loggerFactory)
{
_loader = loader;
_collectionProvider = collectionProvider;
_pageFactoryProvider = pageFactoryProvider;
_modelFactoryProvider = modelFactoryProvider;
_collectionProvider = collectionProvider;
_filterProviders = filterProviders.ToArray();
_valueProviderFactories = valueProviderFactories.ToArray();
_modelMetadataProvider = modelMetadataProvider;
Expand Down Expand Up @@ -157,12 +160,23 @@ private InnerCache CurrentCache
PageTypeInfo = compiledType,
};

var pageFactory = _pageFactoryProvider.CreatePageFactory(compiledActionDescriptor);
var pageDisposer = _pageFactoryProvider.CreatePageDisposer(compiledActionDescriptor);

Func<PageContext, object> modelFactory = null;
Action<PageContext, object> modelReleaser = null;
if (modelType != null)
{
modelFactory = _modelFactoryProvider.CreateModelFactory(compiledActionDescriptor);
modelReleaser = _modelFactoryProvider.CreateModelDisposer(compiledActionDescriptor);
}

return new PageActionInvokerCacheEntry(
compiledActionDescriptor,
_pageFactoryProvider.CreatePageFactory(compiledActionDescriptor),
_pageFactoryProvider.CreatePageDisposer(compiledActionDescriptor),
c => { throw new NotImplementedException(); },
(_, __) => { throw new NotImplementedException(); },
pageFactory,
pageDisposer,
modelFactory,
modelReleaser,
cachedFilters);
}

Expand Down
16 changes: 16 additions & 0 deletions src/Microsoft.AspNetCore.Mvc.RazorPages/PageContextAttribute.cs
@@ -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
{
}
}
Expand Up @@ -8,7 +8,7 @@
using Microsoft.Extensions.Logging;
using Xunit;

namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
public class DefaultPageActivatorTest
{
Expand Down
Expand Up @@ -18,7 +18,7 @@
using Moq;
using Xunit;

namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
{
public class DefaultPageFactoryProviderTest
{
Expand Down

0 comments on commit 3e214e2

Please sign in to comment.