parent
cc584ada39
commit
3e214e2399
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ using System;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <see cref="IPageActivatorProvider"/> that uses type activation to create Pages.
|
/// <see cref="IPageActivatorProvider"/> that uses type activation to create Pages.
|
||||||
|
|
@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Mvc.Rendering;
|
||||||
using Microsoft.AspNetCore.Mvc.Routing;
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
{
|
{
|
||||||
public class DefaultPageFactory : IPageFactoryProvider
|
public class DefaultPageFactory : IPageFactoryProvider
|
||||||
{
|
{
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -24,6 +24,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
private const string ModelPropertyName = "Model";
|
private const string ModelPropertyName = "Model";
|
||||||
private readonly IPageLoader _loader;
|
private readonly IPageLoader _loader;
|
||||||
private readonly IPageFactoryProvider _pageFactoryProvider;
|
private readonly IPageFactoryProvider _pageFactoryProvider;
|
||||||
|
private readonly IPageModelFactoryProvider _modelFactoryProvider;
|
||||||
private readonly IActionDescriptorCollectionProvider _collectionProvider;
|
private readonly IActionDescriptorCollectionProvider _collectionProvider;
|
||||||
private readonly IFilterProvider[] _filterProviders;
|
private readonly IFilterProvider[] _filterProviders;
|
||||||
private readonly IReadOnlyList<IValueProviderFactory> _valueProviderFactories;
|
private readonly IReadOnlyList<IValueProviderFactory> _valueProviderFactories;
|
||||||
|
|
@ -38,6 +39,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
public PageActionInvokerProvider(
|
public PageActionInvokerProvider(
|
||||||
IPageLoader loader,
|
IPageLoader loader,
|
||||||
IPageFactoryProvider pageFactoryProvider,
|
IPageFactoryProvider pageFactoryProvider,
|
||||||
|
IPageModelFactoryProvider modelFactoryProvider,
|
||||||
IActionDescriptorCollectionProvider collectionProvider,
|
IActionDescriptorCollectionProvider collectionProvider,
|
||||||
IEnumerable<IFilterProvider> filterProviders,
|
IEnumerable<IFilterProvider> filterProviders,
|
||||||
IEnumerable<IValueProviderFactory> valueProviderFactories,
|
IEnumerable<IValueProviderFactory> valueProviderFactories,
|
||||||
|
|
@ -49,8 +51,9 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
ILoggerFactory loggerFactory)
|
ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
_loader = loader;
|
_loader = loader;
|
||||||
_collectionProvider = collectionProvider;
|
|
||||||
_pageFactoryProvider = pageFactoryProvider;
|
_pageFactoryProvider = pageFactoryProvider;
|
||||||
|
_modelFactoryProvider = modelFactoryProvider;
|
||||||
|
_collectionProvider = collectionProvider;
|
||||||
_filterProviders = filterProviders.ToArray();
|
_filterProviders = filterProviders.ToArray();
|
||||||
_valueProviderFactories = valueProviderFactories.ToArray();
|
_valueProviderFactories = valueProviderFactories.ToArray();
|
||||||
_modelMetadataProvider = modelMetadataProvider;
|
_modelMetadataProvider = modelMetadataProvider;
|
||||||
|
|
@ -157,12 +160,23 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
PageTypeInfo = compiledType,
|
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(
|
return new PageActionInvokerCacheEntry(
|
||||||
compiledActionDescriptor,
|
compiledActionDescriptor,
|
||||||
_pageFactoryProvider.CreatePageFactory(compiledActionDescriptor),
|
pageFactory,
|
||||||
_pageFactoryProvider.CreatePageDisposer(compiledActionDescriptor),
|
pageDisposer,
|
||||||
c => { throw new NotImplementedException(); },
|
modelFactory,
|
||||||
(_, __) => { throw new NotImplementedException(); },
|
modelReleaser,
|
||||||
cachedFilters);
|
cachedFilters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,7 +8,7 @@ using Microsoft.AspNetCore.Testing;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
{
|
{
|
||||||
public class DefaultPageActivatorTest
|
public class DefaultPageActivatorTest
|
||||||
{
|
{
|
||||||
|
|
@ -18,7 +18,7 @@ using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
{
|
{
|
||||||
public class DefaultPageFactoryProviderTest
|
public class DefaultPageFactoryProviderTest
|
||||||
{
|
{
|
||||||
|
|
@ -0,0 +1,155 @@
|
||||||
|
// 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.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
using Microsoft.AspNetCore.Testing;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
|
{
|
||||||
|
public class DefaultPageModelActivatorProviderTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CreateActivator_ThrowsIfModelTypeInfoOnActionDescriptorIsNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var activatorProvider = new DefaultPageModelActivatorProvider();
|
||||||
|
var actionDescriptor = new CompiledPageActionDescriptor();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
ExceptionAssert.ThrowsArgument(
|
||||||
|
() => activatorProvider.CreateActivator(actionDescriptor),
|
||||||
|
"actionDescriptor",
|
||||||
|
"The 'ModelTypeInfo' property of 'actionDescriptor' must not be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateActivator_CreatesModelInstance()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var activatorProvider = new DefaultPageModelActivatorProvider();
|
||||||
|
var actionDescriptor = new CompiledPageActionDescriptor
|
||||||
|
{
|
||||||
|
ModelTypeInfo = typeof(SimpleModel).GetTypeInfo(),
|
||||||
|
};
|
||||||
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var generator = Mock.Of<IHtmlGenerator>();
|
||||||
|
serviceCollection.AddSingleton(generator);
|
||||||
|
var httpContext = new DefaultHttpContext
|
||||||
|
{
|
||||||
|
RequestServices = serviceCollection.BuildServiceProvider(),
|
||||||
|
};
|
||||||
|
var pageContext = new PageContext
|
||||||
|
{
|
||||||
|
HttpContext = httpContext
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var activator = activatorProvider.CreateActivator(actionDescriptor);
|
||||||
|
var model = activator(pageContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var simpleModel = Assert.IsType<SimpleModel>(model);
|
||||||
|
Assert.NotNull(simpleModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateActivator_TypeActivatesModelType()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var activatorProvider = new DefaultPageModelActivatorProvider();
|
||||||
|
var actionDescriptor = new CompiledPageActionDescriptor
|
||||||
|
{
|
||||||
|
ModelTypeInfo = typeof(ModelWithServices).GetTypeInfo(),
|
||||||
|
};
|
||||||
|
var serviceCollection = new ServiceCollection();
|
||||||
|
var generator = Mock.Of<IHtmlGenerator>();
|
||||||
|
serviceCollection.AddSingleton(generator);
|
||||||
|
var httpContext = new DefaultHttpContext
|
||||||
|
{
|
||||||
|
RequestServices = serviceCollection.BuildServiceProvider(),
|
||||||
|
};
|
||||||
|
var pageContext = new PageContext
|
||||||
|
{
|
||||||
|
HttpContext = httpContext
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var activator = activatorProvider.CreateActivator(actionDescriptor);
|
||||||
|
var model = activator(pageContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var modelWithServices = Assert.IsType<ModelWithServices>(model);
|
||||||
|
Assert.Same(generator, modelWithServices.Generator);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(typeof(SimpleModel))]
|
||||||
|
[InlineData(typeof(object))]
|
||||||
|
public void CreateReleaser_ReturnsNullForModelsThatDoNotImplementDisposable(Type pageType)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = new PageContext();
|
||||||
|
var activator = new DefaultPageActivator();
|
||||||
|
var actionDescriptor = new CompiledPageActionDescriptor
|
||||||
|
{
|
||||||
|
PageTypeInfo = pageType.GetTypeInfo(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var releaser = activator.CreateReleaser(actionDescriptor);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(releaser);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateReleaser_CreatesDelegateThatDisposesDisposableTypes()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var context = new PageContext();
|
||||||
|
var activator = new DefaultPageActivator();
|
||||||
|
var actionDescriptor = new CompiledPageActionDescriptor
|
||||||
|
{
|
||||||
|
PageTypeInfo = typeof(DisposableModel).GetTypeInfo(),
|
||||||
|
};
|
||||||
|
var model = new DisposableModel();
|
||||||
|
|
||||||
|
// Act & Assert
|
||||||
|
var releaser = activator.CreateReleaser(actionDescriptor);
|
||||||
|
releaser(context, model);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(model.Disposed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SimpleModel
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ModelWithServices
|
||||||
|
{
|
||||||
|
public ModelWithServices(IHtmlGenerator generator)
|
||||||
|
{
|
||||||
|
Generator = generator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IHtmlGenerator Generator { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DisposableModel : IDisposable
|
||||||
|
{
|
||||||
|
public bool Disposed { get; private set; }
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,136 @@
|
||||||
|
// 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 Moq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure
|
||||||
|
{
|
||||||
|
public class DefaultPageModelFactoryProviderTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void CreateModelFactory_ReturnsNullFactoryIfModelTypeIsNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = new CompiledPageActionDescriptor();
|
||||||
|
var pageContext = new PageContext();
|
||||||
|
var factoryProvider = CreateModelFactoryProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var factory = factoryProvider.CreateModelFactory(descriptor);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateModelDisposer_ReturnsNullFactoryIfModelTypeIsNull()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = new CompiledPageActionDescriptor();
|
||||||
|
var pageContext = new PageContext();
|
||||||
|
var factoryProvider = CreateModelFactoryProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var disposer = factoryProvider.CreateModelDisposer(descriptor);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Null(disposer);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ModelFactory_InitializesModelInstances()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = new CompiledPageActionDescriptor
|
||||||
|
{
|
||||||
|
ModelTypeInfo = typeof(SimpleModel).GetTypeInfo(),
|
||||||
|
};
|
||||||
|
var pageContext = new PageContext();
|
||||||
|
var factoryProvider = CreateModelFactoryProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var factory = factoryProvider.CreateModelFactory(descriptor);
|
||||||
|
var instance = factory(pageContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var model = Assert.IsType<SimpleModel>(instance);
|
||||||
|
Assert.NotNull(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ModelFactory_InjectsPropertiesWithPageContextAttribute()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = new CompiledPageActionDescriptor
|
||||||
|
{
|
||||||
|
ModelTypeInfo = typeof(ModelWithPageContext).GetTypeInfo(),
|
||||||
|
};
|
||||||
|
var pageContext = new PageContext();
|
||||||
|
var factoryProvider = CreateModelFactoryProvider();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var factory = factoryProvider.CreateModelFactory(descriptor);
|
||||||
|
var instance = factory(pageContext);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var testModel = Assert.IsType<ModelWithPageContext>(instance);
|
||||||
|
Assert.Same(pageContext, testModel.ContextWithAttribute);
|
||||||
|
Assert.Null(testModel.ContextWithoutAttribute);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateModelDisposer_ReturnsDisposerFromModelActivatorProvider()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = new CompiledPageActionDescriptor
|
||||||
|
{
|
||||||
|
ModelTypeInfo = typeof(SimpleModel).GetTypeInfo()
|
||||||
|
};
|
||||||
|
var pageContext = new PageContext();
|
||||||
|
var modelActivatorProvider = new Mock<IPageModelActivatorProvider>();
|
||||||
|
Action<PageContext, object> disposer = (_, __) => { };
|
||||||
|
modelActivatorProvider.Setup(p => p.CreateReleaser(descriptor))
|
||||||
|
.Returns(disposer);
|
||||||
|
var factoryProvider = CreateModelFactoryProvider(modelActivatorProvider.Object);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var actual = factoryProvider.CreateModelDisposer(descriptor);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Same(disposer, actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DefaultPageModelFactoryProvider CreateModelFactoryProvider(
|
||||||
|
IPageModelActivatorProvider modelActivator = null)
|
||||||
|
{
|
||||||
|
if (modelActivator == null)
|
||||||
|
{
|
||||||
|
var mockActivator = new Mock<IPageModelActivatorProvider>();
|
||||||
|
mockActivator.Setup(a => a.CreateActivator(It.IsAny<CompiledPageActionDescriptor>()))
|
||||||
|
.Returns((CompiledPageActionDescriptor descriptor) =>
|
||||||
|
{
|
||||||
|
return (context) => Activator.CreateInstance(descriptor.ModelTypeInfo.AsType());
|
||||||
|
});
|
||||||
|
|
||||||
|
modelActivator = mockActivator.Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DefaultPageModelFactoryProvider(modelActivator);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class SimpleModel
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ModelWithPageContext
|
||||||
|
{
|
||||||
|
[PageContext]
|
||||||
|
public PageContext ContextWithAttribute { get; set; }
|
||||||
|
|
||||||
|
public PageContext ContextWithoutAttribute { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,24 +3,24 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc.Abstractions;
|
using Microsoft.AspNetCore.Mvc.Abstractions;
|
||||||
using Microsoft.AspNetCore.Mvc.Filters;
|
using Microsoft.AspNetCore.Mvc.Filters;
|
||||||
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||||
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.Extensions.Logging.Testing;
|
using Microsoft.Extensions.Logging.Testing;
|
||||||
using Moq;
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Routing;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
{
|
{
|
||||||
public class PageInvokerProviderTest
|
public class PageInvokerProviderTest
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public void OnProvidersExecuting_PopulatesCacheEntry()
|
public void OnProvidersExecuting_WithEmptyModel_PopulatesCacheEntry()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptor = new PageActionDescriptor
|
var descriptor = new PageActionDescriptor
|
||||||
|
|
@ -30,22 +30,23 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
};
|
};
|
||||||
Func<PageContext, object> factory = _ => null;
|
Func<PageContext, object> factory = _ => null;
|
||||||
Action<PageContext, object> releaser = (_, __) => { };
|
Action<PageContext, object> releaser = (_, __) => { };
|
||||||
|
|
||||||
var loader = new Mock<IPageLoader>();
|
var loader = new Mock<IPageLoader>();
|
||||||
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
.Returns(typeof(object));
|
.Returns(typeof(object));
|
||||||
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||||
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||||
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||||
var factoryProvider = new Mock<IPageFactoryProvider>();
|
var pageFactoryProvider = new Mock<IPageFactoryProvider>();
|
||||||
factoryProvider.Setup(f => f.CreatePageFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
pageFactoryProvider.Setup(f => f.CreatePageFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
||||||
.Returns(factory);
|
.Returns(factory);
|
||||||
factoryProvider.Setup(f => f.CreatePageDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
pageFactoryProvider.Setup(f => f.CreatePageDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
||||||
.Returns(releaser);
|
.Returns(releaser);
|
||||||
|
|
||||||
var invokerProvider = CreateInvokerProvider(
|
var invokerProvider = CreateInvokerProvider(
|
||||||
loader.Object,
|
loader.Object,
|
||||||
actionDescriptorProvider.Object,
|
actionDescriptorProvider.Object,
|
||||||
factoryProvider.Object);
|
pageFactoryProvider.Object);
|
||||||
var context = new ActionInvokerProviderContext(
|
var context = new ActionInvokerProviderContext(
|
||||||
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
||||||
|
|
||||||
|
|
@ -60,6 +61,63 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
Assert.Equal(descriptor.RelativePath, compiledPageActionDescriptor.RelativePath);
|
Assert.Equal(descriptor.RelativePath, compiledPageActionDescriptor.RelativePath);
|
||||||
Assert.Same(factory, entry.PageFactory);
|
Assert.Same(factory, entry.PageFactory);
|
||||||
Assert.Same(releaser, entry.ReleasePage);
|
Assert.Same(releaser, entry.ReleasePage);
|
||||||
|
Assert.Null(entry.ModelFactory);
|
||||||
|
Assert.Null(entry.ReleaseModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OnProvidersExecuting_WithModel_PopulatesCacheEntry()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var descriptor = new PageActionDescriptor
|
||||||
|
{
|
||||||
|
RelativePath = "Path1",
|
||||||
|
FilterDescriptors = new FilterDescriptor[0],
|
||||||
|
};
|
||||||
|
Func<PageContext, object> factory = _ => null;
|
||||||
|
Action<PageContext, object> releaser = (_, __) => { };
|
||||||
|
Func<PageContext, object> modelFactory = _ => null;
|
||||||
|
Action<PageContext, object> modelDisposer = (_, __) => { };
|
||||||
|
|
||||||
|
var loader = new Mock<IPageLoader>();
|
||||||
|
loader.Setup(l => l.Load(It.IsAny<PageActionDescriptor>()))
|
||||||
|
.Returns(typeof(PageWithModel));
|
||||||
|
var descriptorCollection = new ActionDescriptorCollection(new[] { descriptor }, version: 1);
|
||||||
|
var actionDescriptorProvider = new Mock<IActionDescriptorCollectionProvider>();
|
||||||
|
actionDescriptorProvider.Setup(p => p.ActionDescriptors).Returns(descriptorCollection);
|
||||||
|
var pageFactoryProvider = new Mock<IPageFactoryProvider>();
|
||||||
|
pageFactoryProvider.Setup(f => f.CreatePageFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
||||||
|
.Returns(factory);
|
||||||
|
pageFactoryProvider.Setup(f => f.CreatePageDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
||||||
|
.Returns(releaser);
|
||||||
|
|
||||||
|
var modelFactoryProvider = new Mock<IPageModelFactoryProvider>();
|
||||||
|
modelFactoryProvider.Setup(f => f.CreateModelFactory(It.IsAny<CompiledPageActionDescriptor>()))
|
||||||
|
.Returns(modelFactory);
|
||||||
|
modelFactoryProvider.Setup(f => f.CreateModelDisposer(It.IsAny<CompiledPageActionDescriptor>()))
|
||||||
|
.Returns(modelDisposer);
|
||||||
|
|
||||||
|
var invokerProvider = CreateInvokerProvider(
|
||||||
|
loader.Object,
|
||||||
|
actionDescriptorProvider.Object,
|
||||||
|
pageFactoryProvider.Object,
|
||||||
|
modelFactoryProvider.Object);
|
||||||
|
var context = new ActionInvokerProviderContext(
|
||||||
|
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
invokerProvider.OnProvidersExecuting(context);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(context.Result);
|
||||||
|
var actionInvoker = Assert.IsType<PageActionInvoker>(context.Result);
|
||||||
|
var entry = actionInvoker.CacheEntry;
|
||||||
|
var compiledPageActionDescriptor = Assert.IsType<CompiledPageActionDescriptor>(entry.ActionDescriptor);
|
||||||
|
Assert.Equal(descriptor.RelativePath, compiledPageActionDescriptor.RelativePath);
|
||||||
|
Assert.Same(factory, entry.PageFactory);
|
||||||
|
Assert.Same(releaser, entry.ReleasePage);
|
||||||
|
Assert.Same(modelFactory, entry.ModelFactory);
|
||||||
|
Assert.Same(modelDisposer, entry.ReleaseModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -80,8 +138,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
var invokerProvider = CreateInvokerProvider(
|
var invokerProvider = CreateInvokerProvider(
|
||||||
loader.Object,
|
loader.Object,
|
||||||
actionDescriptorProvider.Object,
|
actionDescriptorProvider.Object);
|
||||||
Mock.Of<IPageFactoryProvider>());
|
|
||||||
var context = new ActionInvokerProviderContext(
|
var context = new ActionInvokerProviderContext(
|
||||||
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
||||||
|
|
||||||
|
|
@ -124,8 +181,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
.Returns(typeof(object));
|
.Returns(typeof(object));
|
||||||
var invokerProvider = CreateInvokerProvider(
|
var invokerProvider = CreateInvokerProvider(
|
||||||
loader.Object,
|
loader.Object,
|
||||||
actionDescriptorProvider.Object,
|
actionDescriptorProvider.Object);
|
||||||
Mock.Of<IPageFactoryProvider>());
|
|
||||||
var context = new ActionInvokerProviderContext(
|
var context = new ActionInvokerProviderContext(
|
||||||
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
new ActionContext(new DefaultHttpContext(), new RouteData(), descriptor));
|
||||||
|
|
||||||
|
|
@ -150,7 +206,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
private static PageActionInvokerProvider CreateInvokerProvider(
|
private static PageActionInvokerProvider CreateInvokerProvider(
|
||||||
IPageLoader loader,
|
IPageLoader loader,
|
||||||
IActionDescriptorCollectionProvider actionDescriptorProvider,
|
IActionDescriptorCollectionProvider actionDescriptorProvider,
|
||||||
IPageFactoryProvider factoryProvider)
|
IPageFactoryProvider pageProvider = null,
|
||||||
|
IPageModelFactoryProvider modelProvider = null)
|
||||||
{
|
{
|
||||||
var tempDataFactory = new Mock<ITempDataDictionaryFactory>();
|
var tempDataFactory = new Mock<ITempDataDictionaryFactory>();
|
||||||
tempDataFactory.Setup(t => t.GetTempData(It.IsAny<HttpContext>()))
|
tempDataFactory.Setup(t => t.GetTempData(It.IsAny<HttpContext>()))
|
||||||
|
|
@ -158,7 +215,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
|
|
||||||
return new PageActionInvokerProvider(
|
return new PageActionInvokerProvider(
|
||||||
loader,
|
loader,
|
||||||
factoryProvider,
|
pageProvider ?? Mock.Of<IPageFactoryProvider>(),
|
||||||
|
modelProvider ?? Mock.Of<IPageModelFactoryProvider>(),
|
||||||
actionDescriptorProvider,
|
actionDescriptorProvider,
|
||||||
new IFilterProvider[0],
|
new IFilterProvider[0],
|
||||||
new IValueProviderFactory[0],
|
new IValueProviderFactory[0],
|
||||||
|
|
@ -169,5 +227,10 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
||||||
new DiagnosticListener("Microsoft.AspNetCore"),
|
new DiagnosticListener("Microsoft.AspNetCore"),
|
||||||
NullLoggerFactory.Instance);
|
NullLoggerFactory.Instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class PageWithModel
|
||||||
|
{
|
||||||
|
public object Model { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue