Add support for running conventions on controller properties, Razor Page parameter and properties
Fixes #6935
This commit is contained in:
parent
a08fae1a28
commit
dbff416be6
|
|
@ -0,0 +1,21 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows customization of the properties and parameters on controllers and Razor Pages.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To use this interface, create an <see cref="System.Attribute"/> class which implements the interface and
|
||||
/// place it on an action method parameter.
|
||||
/// </remarks>
|
||||
public interface IParameterModelBaseConvention
|
||||
{
|
||||
/// <summary>
|
||||
/// Called to apply the convention to the <see cref="ParameterModelBase"/>.
|
||||
/// </summary>
|
||||
/// <param name="parameter">The <see cref="ParameterModelBase"/>.</param>
|
||||
void Apply(ParameterModelBase parameter);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows customization of the <see cref="ControllerModel"/>.
|
||||
/// Allows customization of the <see cref="ParameterModel"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To use this interface, create an <see cref="System.Attribute"/> class which implements the interface and
|
||||
|
|
@ -15,10 +15,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
/// </remarks>
|
||||
public interface IParameterModelConvention
|
||||
{
|
||||
/// <summary>
|
||||
/// Called to apply the convention to the <see cref="ParameterModel"/>.
|
||||
/// </summary>
|
||||
/// <param name="parameter">The <see cref="ParameterModel"/>.</param>
|
||||
void Apply(ParameterModel parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,33 +5,22 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
[DebuggerDisplay("ParameterModel: Name={ParameterName}")]
|
||||
public class ParameterModel : ICommonModel, IBindingModel
|
||||
public class ParameterModel : ParameterModelBase, ICommonModel
|
||||
{
|
||||
public ParameterModel(
|
||||
ParameterInfo parameterInfo,
|
||||
IReadOnlyList<object> attributes)
|
||||
: base(parameterInfo?.ParameterType, attributes)
|
||||
{
|
||||
if (parameterInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parameterInfo));
|
||||
}
|
||||
|
||||
if (attributes == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(attributes));
|
||||
}
|
||||
|
||||
ParameterInfo = parameterInfo;
|
||||
Properties = new Dictionary<object, object>();
|
||||
Attributes = new List<object>(attributes);
|
||||
ParameterInfo = parameterInfo ?? throw new ArgumentNullException(nameof(parameterInfo));
|
||||
}
|
||||
|
||||
public ParameterModel(ParameterModel other)
|
||||
: base(other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
|
|
@ -39,27 +28,23 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
}
|
||||
|
||||
Action = other.Action;
|
||||
Attributes = new List<object>(other.Attributes);
|
||||
BindingInfo = other.BindingInfo == null ? null : new BindingInfo(other.BindingInfo);
|
||||
ParameterInfo = other.ParameterInfo;
|
||||
ParameterName = other.ParameterName;
|
||||
Properties = new Dictionary<object, object>(other.Properties);
|
||||
}
|
||||
|
||||
public ActionModel Action { get; set; }
|
||||
|
||||
public IReadOnlyList<object> Attributes { get; }
|
||||
public new IDictionary<object, object> Properties => base.Properties;
|
||||
|
||||
public IDictionary<object, object> Properties { get; }
|
||||
public new IReadOnlyList<object> Attributes => base.Attributes;
|
||||
|
||||
MemberInfo ICommonModel.MemberInfo => ParameterInfo.Member;
|
||||
|
||||
string ICommonModel.Name => ParameterName;
|
||||
|
||||
public ParameterInfo ParameterInfo { get; }
|
||||
|
||||
public string ParameterName { get; set; }
|
||||
|
||||
public BindingInfo BindingInfo { get; set; }
|
||||
public string ParameterName
|
||||
{
|
||||
get => Name;
|
||||
set => Name = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
// 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.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
/// <summary>
|
||||
/// A model type for reading and manipulation properties and parameters.
|
||||
/// <para>
|
||||
/// Derived instances of this type represent properties and parameters for controllers, and Razor Pages.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public abstract class ParameterModelBase : IBindingModel
|
||||
{
|
||||
protected ParameterModelBase(
|
||||
Type parameterType,
|
||||
IReadOnlyList<object> attributes)
|
||||
{
|
||||
ParameterType = parameterType ?? throw new ArgumentNullException(nameof(parameterType));
|
||||
Attributes = new List<object>(attributes ?? throw new ArgumentNullException(nameof(attributes)));
|
||||
|
||||
Properties = new Dictionary<object, object>();
|
||||
}
|
||||
|
||||
protected ParameterModelBase(ParameterModelBase other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(other));
|
||||
}
|
||||
|
||||
ParameterType = other.ParameterType;
|
||||
Attributes = new List<object>(other.Attributes);
|
||||
BindingInfo = other.BindingInfo == null ? null : new BindingInfo(other.BindingInfo);
|
||||
Name = other.Name;
|
||||
Properties = new Dictionary<object, object>(other.Properties);
|
||||
}
|
||||
|
||||
public IReadOnlyList<object> Attributes { get; }
|
||||
|
||||
public IDictionary<object, object> Properties { get; }
|
||||
|
||||
public Type ParameterType { get; }
|
||||
|
||||
public string Name { get; protected set; }
|
||||
|
||||
public BindingInfo BindingInfo { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
/// A type which is used to represent a property in a <see cref="ControllerModel"/>.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("PropertyModel: Name={PropertyName}")]
|
||||
public class PropertyModel : ICommonModel, IBindingModel
|
||||
public class PropertyModel : ParameterModelBase, ICommonModel, IBindingModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="PropertyModel"/>.
|
||||
|
|
@ -23,20 +23,9 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
public PropertyModel(
|
||||
PropertyInfo propertyInfo,
|
||||
IReadOnlyList<object> attributes)
|
||||
: base(propertyInfo?.PropertyType, attributes)
|
||||
{
|
||||
if (propertyInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(propertyInfo));
|
||||
}
|
||||
|
||||
if (attributes == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(attributes));
|
||||
}
|
||||
|
||||
PropertyInfo = propertyInfo;
|
||||
Properties = new Dictionary<object, object>();
|
||||
Attributes = new List<object>(attributes);
|
||||
PropertyInfo = propertyInfo ?? throw new ArgumentNullException(nameof(propertyInfo));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -44,6 +33,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
/// </summary>
|
||||
/// <param name="other">The <see cref="PropertyModel"/> which needs to be copied.</param>
|
||||
public PropertyModel(PropertyModel other)
|
||||
: base(other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
|
|
@ -51,11 +41,8 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
}
|
||||
|
||||
Controller = other.Controller;
|
||||
Attributes = new List<object>(other.Attributes);
|
||||
BindingInfo = BindingInfo == null ? null : new BindingInfo(other.BindingInfo);
|
||||
PropertyInfo = other.PropertyInfo;
|
||||
PropertyName = other.PropertyName;
|
||||
Properties = new Dictionary<object, object>(other.Properties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -63,30 +50,18 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
/// </summary>
|
||||
public ControllerModel Controller { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets any attributes which are annotated on the property.
|
||||
/// </summary>
|
||||
public IReadOnlyList<object> Attributes { get; }
|
||||
|
||||
public IDictionary<object, object> Properties { get; }
|
||||
|
||||
MemberInfo ICommonModel.MemberInfo => PropertyInfo;
|
||||
|
||||
string ICommonModel.Name => PropertyName;
|
||||
public new IDictionary<object, object> Properties => base.Properties;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="BindingInfo"/> associated with this model.
|
||||
/// </summary>
|
||||
public BindingInfo BindingInfo { get; set; }
|
||||
public new IReadOnlyList<object> Attributes => base.Attributes;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying <see cref="PropertyInfo"/>.
|
||||
/// </summary>
|
||||
public PropertyInfo PropertyInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the property represented by this model.
|
||||
/// </summary>
|
||||
public string PropertyName { get; set; }
|
||||
public string PropertyName
|
||||
{
|
||||
get => Name;
|
||||
set => Name = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Mvc.ApplicationModels;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
|
|
@ -18,7 +18,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
/// </summary>
|
||||
/// <param name="list">The list of <see cref="IApplicationModelConvention"/>s.</param>
|
||||
/// <typeparam name="TApplicationModelConvention">The type to remove.</typeparam>
|
||||
public static void RemoveType<TApplicationModelConvention>(this IList<IApplicationModelConvention> list) where TApplicationModelConvention : IApplicationModelConvention
|
||||
public static void RemoveType<TApplicationModelConvention>(this IList<IApplicationModelConvention> list)
|
||||
where TApplicationModelConvention : IApplicationModelConvention
|
||||
{
|
||||
if (list == null)
|
||||
{
|
||||
|
|
@ -127,17 +128,36 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
conventions.Add(new ParameterApplicationModelConvention(parameterModelConvention));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="IParameterModelBaseConvention"/> to all properties and parameters in the application.
|
||||
/// </summary>
|
||||
/// <param name="conventions">The list of <see cref="IApplicationModelConvention"/>
|
||||
/// in <see cref="AspNetCore.Mvc.MvcOptions"/>.</param>
|
||||
/// <param name="parameterModelConvention">The <see cref="IParameterModelBaseConvention"/> which needs to be
|
||||
/// added.</param>
|
||||
public static void Add(
|
||||
this IList<IApplicationModelConvention> conventions,
|
||||
IParameterModelBaseConvention parameterModelConvention)
|
||||
{
|
||||
if (conventions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(conventions));
|
||||
}
|
||||
|
||||
if (parameterModelConvention == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parameterModelConvention));
|
||||
}
|
||||
|
||||
conventions.Add(new ParameterBaseApplicationModelConvention(parameterModelConvention));
|
||||
}
|
||||
|
||||
private class ParameterApplicationModelConvention : IApplicationModelConvention
|
||||
{
|
||||
private readonly IParameterModelConvention _parameterModelConvention;
|
||||
|
||||
public ParameterApplicationModelConvention(IParameterModelConvention parameterModelConvention)
|
||||
{
|
||||
if (parameterModelConvention == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parameterModelConvention));
|
||||
}
|
||||
|
||||
_parameterModelConvention = parameterModelConvention;
|
||||
}
|
||||
|
||||
|
|
@ -167,6 +187,36 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
}
|
||||
|
||||
private class ParameterBaseApplicationModelConvention :
|
||||
IApplicationModelConvention, IParameterModelBaseConvention
|
||||
{
|
||||
private readonly IParameterModelBaseConvention _parameterBaseModelConvention;
|
||||
|
||||
public ParameterBaseApplicationModelConvention(IParameterModelBaseConvention parameterModelBaseConvention)
|
||||
{
|
||||
_parameterBaseModelConvention = parameterModelBaseConvention;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Apply(ApplicationModel application)
|
||||
{
|
||||
if (application == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(application));
|
||||
}
|
||||
}
|
||||
|
||||
void IParameterModelBaseConvention.Apply(ParameterModelBase parameterModel)
|
||||
{
|
||||
if (parameterModel == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parameterModel));
|
||||
}
|
||||
|
||||
_parameterBaseModelConvention.Apply(parameterModel);
|
||||
}
|
||||
}
|
||||
|
||||
private class ActionApplicationModelConvention : IApplicationModelConvention
|
||||
{
|
||||
private readonly IActionModelConvention _actionModelConvention;
|
||||
|
|
|
|||
|
|
@ -39,8 +39,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
convention.Apply(applicationModel);
|
||||
}
|
||||
|
||||
var controllers = applicationModel.Controllers.ToArray();
|
||||
// First apply the conventions from attributes in decreasing order of scope.
|
||||
foreach (var controller in applicationModel.Controllers)
|
||||
foreach (var controller in controllers)
|
||||
{
|
||||
// ToArray is needed here to prevent issues with modifying the attributes collection
|
||||
// while iterating it.
|
||||
|
|
@ -54,7 +55,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
controllerConvention.Apply(controller);
|
||||
}
|
||||
|
||||
foreach (var action in controller.Actions)
|
||||
var actions = controller.Actions.ToArray();
|
||||
foreach (var action in actions)
|
||||
{
|
||||
// ToArray is needed here to prevent issues with modifying the attributes collection
|
||||
// while iterating it.
|
||||
|
|
@ -68,7 +70,8 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
actionConvention.Apply(action);
|
||||
}
|
||||
|
||||
foreach (var parameter in action.Parameters)
|
||||
var parameters = action.Parameters.ToArray();
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
// ToArray is needed here to prevent issues with modifying the attributes collection
|
||||
// while iterating it.
|
||||
|
|
@ -81,9 +84,35 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
|||
{
|
||||
parameterConvention.Apply(parameter);
|
||||
}
|
||||
|
||||
var parameterBaseConventions = GetConventions<IParameterModelBaseConvention>(conventions, parameter.Attributes);
|
||||
foreach (var parameterConvention in parameterBaseConventions)
|
||||
{
|
||||
parameterConvention.Apply(parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var properties = controller.ControllerProperties.ToArray();
|
||||
foreach (var property in properties)
|
||||
{
|
||||
var parameterBaseConventions = GetConventions<IParameterModelBaseConvention>(conventions, property.Attributes);
|
||||
|
||||
foreach (var parameterConvention in parameterBaseConventions)
|
||||
{
|
||||
parameterConvention.Apply(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<TConvention> GetConventions<TConvention>(
|
||||
IEnumerable<IApplicationModelConvention> conventions,
|
||||
IReadOnlyList<object> attributes)
|
||||
{
|
||||
return Enumerable.Concat(
|
||||
conventions.OfType<TConvention>(),
|
||||
attributes.OfType<TConvention>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows customization of the <see cref="PageHandlerModel"/>.
|
||||
/// </summary>
|
||||
public interface IPageHandlerModelConvention : IPageConvention
|
||||
{
|
||||
/// <summary>
|
||||
/// Called to apply the convention to the <see cref="PageHandlerModel"/>.
|
||||
/// </summary>
|
||||
/// <param name="model">The <see cref="PageHandlerModel"/>.</param>
|
||||
void Apply(PageHandlerModel model);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,29 +5,32 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
||||
{
|
||||
[DebuggerDisplay("PageParameterModel: Name={ParameterName}")]
|
||||
public class PageParameterModel : ICommonModel, IBindingModel
|
||||
public class PageParameterModel : ParameterModelBase, ICommonModel, IBindingModel
|
||||
{
|
||||
public PageParameterModel(
|
||||
ParameterInfo parameterInfo,
|
||||
IReadOnlyList<object> attributes)
|
||||
: base(parameterInfo?.ParameterType, attributes)
|
||||
{
|
||||
ParameterInfo = parameterInfo ?? throw new ArgumentNullException(nameof(parameterInfo));
|
||||
if (parameterInfo == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(parameterInfo));
|
||||
}
|
||||
|
||||
if (attributes == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(attributes));
|
||||
}
|
||||
|
||||
Properties = new Dictionary<object, object>();
|
||||
Attributes = new List<object>(attributes);
|
||||
ParameterInfo = parameterInfo;
|
||||
}
|
||||
|
||||
public PageParameterModel(PageParameterModel other)
|
||||
: base(other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
|
|
@ -35,27 +38,19 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
}
|
||||
|
||||
Handler = other.Handler;
|
||||
Attributes = new List<object>(other.Attributes);
|
||||
BindingInfo = other.BindingInfo == null ? null : new BindingInfo(other.BindingInfo);
|
||||
ParameterInfo = other.ParameterInfo;
|
||||
ParameterName = other.ParameterName;
|
||||
Properties = new Dictionary<object, object>(other.Properties);
|
||||
}
|
||||
|
||||
public PageHandlerModel Handler { get; set; }
|
||||
|
||||
public IReadOnlyList<object> Attributes { get; }
|
||||
|
||||
public IDictionary<object, object> Properties { get; }
|
||||
|
||||
MemberInfo ICommonModel.MemberInfo => ParameterInfo.Member;
|
||||
|
||||
string ICommonModel.Name => ParameterName;
|
||||
|
||||
public ParameterInfo ParameterInfo { get; }
|
||||
|
||||
public string ParameterName { get; set; }
|
||||
|
||||
public BindingInfo BindingInfo { get; set; }
|
||||
public string ParameterName
|
||||
{
|
||||
get => Name;
|
||||
set => Name = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
/// Represents a property in a <see cref="PageApplicationModel"/>.
|
||||
/// </summary>
|
||||
[DebuggerDisplay("PagePropertyModel: Name={PropertyName}")]
|
||||
public class PagePropertyModel : ICommonModel, IBindingModel
|
||||
public class PagePropertyModel : ParameterModelBase, ICommonModel
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="PagePropertyModel"/>.
|
||||
|
|
@ -23,10 +23,9 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
public PagePropertyModel(
|
||||
PropertyInfo propertyInfo,
|
||||
IReadOnlyList<object> attributes)
|
||||
: base(propertyInfo?.PropertyType, attributes)
|
||||
{
|
||||
PropertyInfo = propertyInfo ?? throw new ArgumentNullException(nameof(propertyInfo));
|
||||
Properties = new Dictionary<object, object>();
|
||||
Attributes = new List<object>(attributes) ?? throw new ArgumentNullException(nameof(attributes));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -34,6 +33,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
/// </summary>
|
||||
/// <param name="other">The <see cref="PagePropertyModel"/> which needs to be copied.</param>
|
||||
public PagePropertyModel(PagePropertyModel other)
|
||||
: base(other)
|
||||
{
|
||||
if (other == null)
|
||||
{
|
||||
|
|
@ -41,11 +41,8 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
}
|
||||
|
||||
Page = other.Page;
|
||||
Attributes = new List<object>(other.Attributes);
|
||||
BindingInfo = BindingInfo == null ? null : new BindingInfo(other.BindingInfo);
|
||||
PropertyInfo = other.PropertyInfo;
|
||||
PropertyName = other.PropertyName;
|
||||
Properties = new Dictionary<object, object>(other.Properties);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -53,31 +50,14 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
|
|||
/// </summary>
|
||||
public PageApplicationModel Page { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets any attributes which are annotated on the property.
|
||||
/// </summary>
|
||||
public IReadOnlyList<object> Attributes { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDictionary<object, object> Properties { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="BindingInfo"/> associated with this model.
|
||||
/// </summary>
|
||||
public BindingInfo BindingInfo { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying <see cref="PropertyInfo"/>.
|
||||
/// </summary>
|
||||
public PropertyInfo PropertyInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the property represented by this model.
|
||||
/// </summary>
|
||||
public string PropertyName { get; set; }
|
||||
|
||||
MemberInfo ICommonModel.MemberInfo => PropertyInfo;
|
||||
|
||||
string ICommonModel.Name => PropertyName;
|
||||
public PropertyInfo PropertyInfo { get; }
|
||||
|
||||
public string PropertyName
|
||||
{
|
||||
get => Name;
|
||||
set => Name = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -143,6 +143,30 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
return conventions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified <paramref name="convention"/> to <paramref name="conventions"/>.
|
||||
/// The added convention will apply to all handler properties and parameters on handler methods.
|
||||
/// </summary>
|
||||
/// <param name="conventions">The <see cref="PageConventionCollection"/> to configure.</param>
|
||||
/// <param name="convention">The <see cref="IParameterModelBaseConvention"/> to apply.</param>
|
||||
/// <returns>The <see cref="PageConventionCollection"/>.</returns>
|
||||
public static PageConventionCollection Add(this PageConventionCollection conventions, IParameterModelBaseConvention convention)
|
||||
{
|
||||
if (conventions == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(conventions));
|
||||
}
|
||||
|
||||
if (convention == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(convention));
|
||||
}
|
||||
|
||||
var adapter = new ParameterModelBaseConventionAdapter(convention);
|
||||
conventions.Add(adapter);
|
||||
return conventions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a <see cref="AllowAnonymousFilter"/> to all pages under the specified area folder.
|
||||
/// </summary>
|
||||
|
|
@ -461,5 +485,20 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
});
|
||||
};
|
||||
}
|
||||
|
||||
private class ParameterModelBaseConventionAdapter : IPageConvention, IParameterModelBaseConvention
|
||||
{
|
||||
private readonly IParameterModelBaseConvention _convention;
|
||||
|
||||
public ParameterModelBaseConventionAdapter(IParameterModelBaseConvention convention)
|
||||
{
|
||||
_convention = convention;
|
||||
}
|
||||
|
||||
public void Apply(ParameterModelBase parameter)
|
||||
{
|
||||
_convention.Apply(parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
{
|
||||
private readonly IPageApplicationModelProvider[] _applicationModelProviders;
|
||||
private readonly IViewCompilerProvider _viewCompilerProvider;
|
||||
private readonly IPageApplicationModelConvention[] _conventions;
|
||||
private readonly PageConventionCollection _conventions;
|
||||
private readonly FilterCollection _globalFilters;
|
||||
|
||||
public DefaultPageLoader(
|
||||
|
|
@ -30,9 +30,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
.OrderBy(p => p.Order)
|
||||
.ToArray();
|
||||
_viewCompilerProvider = viewCompilerProvider;
|
||||
_conventions = pageOptions.Value.Conventions
|
||||
.OfType<IPageApplicationModelConvention>()
|
||||
.ToArray();
|
||||
_conventions = pageOptions.Value.Conventions;
|
||||
_globalFilters = mvcOptions.Value.Filters;
|
||||
}
|
||||
|
||||
|
|
@ -60,12 +58,58 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
_applicationModelProviders[i].OnProvidersExecuted(context);
|
||||
}
|
||||
|
||||
for (var i = 0; i < _conventions.Length; i++)
|
||||
{
|
||||
_conventions[i].Apply(context.PageApplicationModel);
|
||||
}
|
||||
ApplyConventions(_conventions, context.PageApplicationModel);
|
||||
|
||||
return CompiledPageActionDescriptorBuilder.Build(context.PageApplicationModel, _globalFilters);
|
||||
}
|
||||
|
||||
internal static void ApplyConventions(
|
||||
PageConventionCollection conventions,
|
||||
PageApplicationModel pageApplicationModel)
|
||||
{
|
||||
var applicationModelConventions = GetConventions<IPageApplicationModelConvention>(pageApplicationModel.HandlerTypeAttributes);
|
||||
foreach (var convention in applicationModelConventions)
|
||||
{
|
||||
convention.Apply(pageApplicationModel);
|
||||
}
|
||||
|
||||
var handlers = pageApplicationModel.HandlerMethods.ToArray();
|
||||
foreach (var handlerModel in handlers)
|
||||
{
|
||||
var handlerModelConventions = GetConventions<IPageHandlerModelConvention>(handlerModel.Attributes);
|
||||
foreach (var convention in handlerModelConventions)
|
||||
{
|
||||
convention.Apply(handlerModel);
|
||||
}
|
||||
|
||||
var parameterModels = handlerModel.Parameters.ToArray();
|
||||
foreach (var parameterModel in parameterModels)
|
||||
{
|
||||
var parameterModelConventions = GetConventions<IParameterModelBaseConvention>(parameterModel.Attributes);
|
||||
foreach (var convention in parameterModelConventions)
|
||||
{
|
||||
convention.Apply(parameterModel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var properties = pageApplicationModel.HandlerProperties.ToArray();
|
||||
foreach (var propertyModel in properties)
|
||||
{
|
||||
var propertyModelConventions = GetConventions<IParameterModelBaseConvention>(propertyModel.Attributes);
|
||||
foreach (var convention in propertyModelConventions)
|
||||
{
|
||||
convention.Apply(propertyModel);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<TConvention> GetConventions<TConvention>(
|
||||
IReadOnlyList<object> attributes)
|
||||
{
|
||||
return Enumerable.Concat(
|
||||
conventions.OfType<TConvention>(),
|
||||
attributes.OfType<TConvention>());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,8 +18,16 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
{
|
||||
// Arrange
|
||||
var app = new ApplicationModel();
|
||||
app.Controllers.Add(new ControllerModel(typeof(HelloController).GetTypeInfo(), new List<object>()));
|
||||
app.Controllers.Add(new ControllerModel(typeof(WorldController).GetTypeInfo(), new List<object>()));
|
||||
var controllerType = typeof(HelloController);
|
||||
var controllerModel = new ControllerModel(controllerType.GetTypeInfo(), Array.Empty<object>());
|
||||
app.Controllers.Add(controllerModel);
|
||||
|
||||
var actionModel = new ActionModel(controllerType.GetMethod(nameof(HelloController.GetInfo)), Array.Empty<object>());
|
||||
controllerModel.Actions.Add(actionModel);
|
||||
var parameterModel = new ParameterModel(
|
||||
controllerType.GetMethod(nameof(HelloController.GetInfo)).GetParameters()[0],
|
||||
Array.Empty<object>());
|
||||
actionModel.Parameters.Add(parameterModel);
|
||||
|
||||
var options = new MvcOptions();
|
||||
options.Conventions.Add(new SimpleParameterConvention());
|
||||
|
|
@ -28,18 +36,9 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
options.Conventions[0].Apply(app);
|
||||
|
||||
// Assert
|
||||
foreach (var controller in app.Controllers)
|
||||
{
|
||||
foreach (var action in controller.Actions)
|
||||
{
|
||||
foreach (var parameter in action.Parameters)
|
||||
{
|
||||
var kvp = Assert.Single(parameter.Properties);
|
||||
Assert.Equal("TestProperty", kvp.Key);
|
||||
Assert.Equal("TestValue", kvp.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
var kvp = Assert.Single(parameterModel.Properties);
|
||||
Assert.Equal("TestProperty", kvp.Key);
|
||||
Assert.Equal("TestValue", kvp.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -47,8 +46,28 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
{
|
||||
// Arrange
|
||||
var app = new ApplicationModel();
|
||||
app.Controllers.Add(new ControllerModel(typeof(HelloController).GetTypeInfo(), new List<object>()));
|
||||
app.Controllers.Add(new ControllerModel(typeof(WorldController).GetTypeInfo(), new List<object>()));
|
||||
var controllerType1 = typeof(HelloController).GetTypeInfo();
|
||||
var actionMethod1 = controllerType1.GetMethod(nameof(HelloController.GetHello));
|
||||
var controllerModel1 = new ControllerModel(controllerType1, Array.Empty<object>())
|
||||
{
|
||||
Actions =
|
||||
{
|
||||
new ActionModel(actionMethod1, Array.Empty<object>()),
|
||||
}
|
||||
};
|
||||
|
||||
var controllerType2 = typeof(WorldController).GetTypeInfo();
|
||||
var actionMethod2 = controllerType2.GetMethod(nameof(WorldController.GetWorld));
|
||||
var controllerModel2 = new ControllerModel(controllerType2, Array.Empty<object>())
|
||||
{
|
||||
Actions =
|
||||
{
|
||||
new ActionModel(actionMethod2, Array.Empty<object>()),
|
||||
},
|
||||
};
|
||||
|
||||
app.Controllers.Add(controllerModel1);
|
||||
app.Controllers.Add(controllerModel2);
|
||||
|
||||
var options = new MvcOptions();
|
||||
options.Conventions.Add(new SimpleActionConvention());
|
||||
|
|
@ -57,15 +76,76 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
options.Conventions[0].Apply(app);
|
||||
|
||||
// Assert
|
||||
foreach (var controller in app.Controllers)
|
||||
var kvp = Assert.Single(controllerModel1.Actions[0].Properties);
|
||||
Assert.Equal("TestProperty", kvp.Key);
|
||||
Assert.Equal("TestValue", kvp.Value);
|
||||
|
||||
kvp = Assert.Single(controllerModel2.Actions[0].Properties);
|
||||
Assert.Equal("TestProperty", kvp.Key);
|
||||
Assert.Equal("TestValue", kvp.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddedParameterConvention_AppliesToAllPropertiesAndParameters()
|
||||
{
|
||||
// Arrange
|
||||
var app = new ApplicationModel();
|
||||
var controllerType1 = typeof(HelloController).GetTypeInfo();
|
||||
var parameterModel1 = new ParameterModel(
|
||||
controllerType1.GetMethod(nameof(HelloController.GetInfo)).GetParameters()[0],
|
||||
Array.Empty<object>());
|
||||
var actionMethod1 = controllerType1.GetMethod(nameof(HelloController.GetInfo));
|
||||
var property1 = controllerType1.GetProperty(nameof(HelloController.Property1));
|
||||
var controllerModel1 = new ControllerModel(controllerType1, Array.Empty<object>())
|
||||
{
|
||||
foreach (var action in controller.Actions)
|
||||
ControllerProperties =
|
||||
{
|
||||
var kvp = Assert.Single(action.Properties);
|
||||
Assert.Equal("TestProperty", kvp.Key);
|
||||
Assert.Equal("TestValue", kvp.Value);
|
||||
new PropertyModel(property1, Array.Empty<object>()),
|
||||
},
|
||||
Actions =
|
||||
{
|
||||
new ActionModel(actionMethod1, Array.Empty<object>())
|
||||
{
|
||||
Parameters =
|
||||
{
|
||||
parameterModel1,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var controllerType2 = typeof(WorldController).GetTypeInfo();
|
||||
var property2 = controllerType2.GetProperty(nameof(WorldController.Property2));
|
||||
var controllerModel2 = new ControllerModel(controllerType2, Array.Empty<object>())
|
||||
{
|
||||
ControllerProperties =
|
||||
{
|
||||
new PropertyModel(property2, Array.Empty<object>()),
|
||||
},
|
||||
};
|
||||
|
||||
app.Controllers.Add(controllerModel1);
|
||||
app.Controllers.Add(controllerModel2);
|
||||
|
||||
var options = new MvcOptions();
|
||||
var convention = new SimplePropertyConvention();
|
||||
options.Conventions.Add(convention);
|
||||
|
||||
// Act
|
||||
ApplicationModelConventions.ApplyConventions(app, options.Conventions);
|
||||
|
||||
// Assert
|
||||
var kvp = Assert.Single(controllerModel1.ControllerProperties[0].Properties);
|
||||
Assert.Equal("TestProperty", kvp.Key);
|
||||
Assert.Equal("TestValue", kvp.Value);
|
||||
|
||||
kvp = Assert.Single(controllerModel2.ControllerProperties[0].Properties);
|
||||
Assert.Equal("TestProperty", kvp.Key);
|
||||
Assert.Equal("TestValue", kvp.Value);
|
||||
|
||||
kvp = Assert.Single(controllerModel1.Actions[0].Parameters[0].Properties);
|
||||
Assert.Equal("TestProperty", kvp.Key);
|
||||
Assert.Equal("TestValue", kvp.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -74,8 +154,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
// Arrange
|
||||
var options = new MvcOptions();
|
||||
var app = new ApplicationModel();
|
||||
app.Controllers.Add(new ControllerModel(typeof(HelloController).GetTypeInfo(), new List<object>()));
|
||||
app.Controllers.Add(new ControllerModel(typeof(WorldController).GetTypeInfo(), new List<object>()));
|
||||
app.Controllers.Add(new ControllerModel(typeof(HelloController).GetTypeInfo(), Array.Empty<object>()));
|
||||
app.Controllers.Add(new ControllerModel(typeof(WorldController).GetTypeInfo(), Array.Empty<object>()));
|
||||
options.Conventions.Add(new SimpleControllerConvention());
|
||||
|
||||
// Act
|
||||
|
|
@ -115,7 +195,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
// Arrange
|
||||
var applicationModel = new ApplicationModel();
|
||||
applicationModel.Controllers.Add(
|
||||
new ControllerModel(typeof(HelloController).GetTypeInfo(), new List<object>())
|
||||
new ControllerModel(typeof(HelloController).GetTypeInfo(), Array.Empty<object>())
|
||||
{
|
||||
Application = applicationModel
|
||||
});
|
||||
|
|
@ -128,18 +208,36 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
ApplicationModelConventions.ApplyConventions(applicationModel, conventions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplicationModelConventions_CopiesControllerModelCollectionOnApply_WhenRegisteredAsAnAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var controllerModelConvention = new ControllerModelCollectionModifyingConvention();
|
||||
var applicationModel = new ApplicationModel();
|
||||
applicationModel.Controllers.Add(
|
||||
new ControllerModel(typeof(HelloController).GetTypeInfo(), new[] { controllerModelConvention })
|
||||
{
|
||||
Application = applicationModel
|
||||
});
|
||||
|
||||
var conventions = new List<IApplicationModelConvention>();
|
||||
|
||||
// Act & Assert
|
||||
ApplicationModelConventions.ApplyConventions(applicationModel, conventions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplicationModelConventions_CopiesActionModelCollectionOnApply()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(HelloController).GetTypeInfo();
|
||||
var applicationModel = new ApplicationModel();
|
||||
var controllerModel = new ControllerModel(controllerType, new List<object>())
|
||||
var controllerModel = new ControllerModel(controllerType, Array.Empty<object>())
|
||||
{
|
||||
Application = applicationModel
|
||||
};
|
||||
controllerModel.Actions.Add(
|
||||
new ActionModel(controllerType.GetMethod(nameof(HelloController.GetHello)), new List<object>())
|
||||
new ActionModel(controllerType.GetMethod(nameof(HelloController.GetHello)), Array.Empty<object>())
|
||||
{
|
||||
Controller = controllerModel
|
||||
});
|
||||
|
|
@ -153,25 +251,74 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
ApplicationModelConventions.ApplyConventions(applicationModel, conventions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplicationModelConventions_CopiesPropertyModelCollectionOnApply()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(HelloController).GetTypeInfo();
|
||||
var applicationModel = new ApplicationModel();
|
||||
var controllerModel = new ControllerModel(controllerType, Array.Empty<object>())
|
||||
{
|
||||
Application = applicationModel
|
||||
};
|
||||
controllerModel.ControllerProperties.Add(
|
||||
new PropertyModel(controllerType.GetProperty(nameof(HelloController.Property1)), Array.Empty<object>())
|
||||
{
|
||||
Controller = controllerModel
|
||||
});
|
||||
applicationModel.Controllers.Add(controllerModel);
|
||||
|
||||
var propertyModelConvention = new ParameterModelBaseConvention();
|
||||
var conventions = new List<IApplicationModelConvention>();
|
||||
conventions.Add(propertyModelConvention);
|
||||
|
||||
// Act & Assert
|
||||
ApplicationModelConventions.ApplyConventions(applicationModel, conventions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplicationModelConventions_CopiesPropertyModelCollectionOnApply_WhenAppliedViaAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var propertyModelConvention = new ParameterModelBaseConvention();
|
||||
var controllerType = typeof(HelloController).GetTypeInfo();
|
||||
var applicationModel = new ApplicationModel();
|
||||
var controllerModel = new ControllerModel(controllerType, Array.Empty<object>())
|
||||
{
|
||||
Application = applicationModel
|
||||
};
|
||||
controllerModel.ControllerProperties.Add(
|
||||
new PropertyModel(controllerType.GetProperty(nameof(HelloController.Property1)), new[] { propertyModelConvention })
|
||||
{
|
||||
Controller = controllerModel
|
||||
});
|
||||
applicationModel.Controllers.Add(controllerModel);
|
||||
|
||||
var conventions = new List<IApplicationModelConvention>();
|
||||
|
||||
// Act & Assert
|
||||
ApplicationModelConventions.ApplyConventions(applicationModel, conventions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplicationModelConventions_CopiesParameterModelCollectionOnApply()
|
||||
{
|
||||
// Arrange
|
||||
var controllerType = typeof(HelloController).GetTypeInfo();
|
||||
var app = new ApplicationModel();
|
||||
var controllerModel = new ControllerModel(controllerType, new List<object>())
|
||||
var controllerModel = new ControllerModel(controllerType, Array.Empty<object>())
|
||||
{
|
||||
Application = app
|
||||
};
|
||||
app.Controllers.Add(controllerModel);
|
||||
var actionModel = new ActionModel(controllerType.GetMethod(nameof(HelloController.GetInfo)), new List<object>())
|
||||
var actionModel = new ActionModel(controllerType.GetMethod(nameof(HelloController.GetInfo)), Array.Empty<object>())
|
||||
{
|
||||
Controller = controllerModel
|
||||
};
|
||||
controllerModel.Actions.Add(actionModel);
|
||||
var parameterModel = new ParameterModel(
|
||||
controllerType.GetMethod(nameof(HelloController.GetInfo)).GetParameters()[0],
|
||||
new List<object>())
|
||||
Array.Empty<object>())
|
||||
{
|
||||
Action = actionModel
|
||||
};
|
||||
|
|
@ -185,6 +332,37 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
ApplicationModelConventions.ApplyConventions(app, conventions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplicationModelConventions_CopiesParameterModelCollectionOnApply_WhenRegisteredViaAttribute()
|
||||
{
|
||||
// Arrange
|
||||
var parameterModelConvention = new ParameterModelCollectionModifyingConvention();
|
||||
var controllerType = typeof(HelloController).GetTypeInfo();
|
||||
var app = new ApplicationModel();
|
||||
var controllerModel = new ControllerModel(controllerType, Array.Empty<object>())
|
||||
{
|
||||
Application = app
|
||||
};
|
||||
app.Controllers.Add(controllerModel);
|
||||
var actionModel = new ActionModel(controllerType.GetMethod(nameof(HelloController.GetInfo)), Array.Empty<object>())
|
||||
{
|
||||
Controller = controllerModel
|
||||
};
|
||||
controllerModel.Actions.Add(actionModel);
|
||||
var parameterModel = new ParameterModel(
|
||||
controllerType.GetMethod(nameof(HelloController.GetInfo)).GetParameters()[0],
|
||||
new[] { parameterModelConvention })
|
||||
{
|
||||
Action = actionModel
|
||||
};
|
||||
actionModel.Parameters.Add(parameterModel);
|
||||
|
||||
var conventions = new List<IApplicationModelConvention>();
|
||||
|
||||
// Act & Assert
|
||||
ApplicationModelConventions.ApplyConventions(app, conventions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GenericRemoveType_RemovesAllOfType()
|
||||
{
|
||||
|
|
@ -222,6 +400,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
|
||||
private class HelloController
|
||||
{
|
||||
public string Property1 { get; set; }
|
||||
|
||||
public string GetHello()
|
||||
{
|
||||
return "Hello";
|
||||
|
|
@ -235,6 +415,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
|
||||
private class WorldController
|
||||
{
|
||||
public string Property2 { get; set; }
|
||||
|
||||
public string GetWorld()
|
||||
{
|
||||
return "World!";
|
||||
|
|
@ -257,6 +439,14 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
}
|
||||
|
||||
private class SimplePropertyConvention : IParameterModelBaseConvention
|
||||
{
|
||||
public void Apply(ParameterModelBase action)
|
||||
{
|
||||
action.Properties.Add("TestProperty", "TestValue");
|
||||
}
|
||||
}
|
||||
|
||||
private class SimpleControllerConvention : IControllerModelConvention
|
||||
{
|
||||
public void Apply(ControllerModel controller)
|
||||
|
|
@ -289,6 +479,15 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
}
|
||||
}
|
||||
|
||||
private class ParameterModelBaseConvention : IParameterModelBaseConvention
|
||||
{
|
||||
public void Apply(ParameterModelBase modelBase)
|
||||
{
|
||||
var property = (PropertyModel)modelBase;
|
||||
property.Controller.ControllerProperties.Remove(property);
|
||||
}
|
||||
}
|
||||
|
||||
private class ParameterModelCollectionModifyingConvention : IParameterModelConvention
|
||||
{
|
||||
public void Apply(ParameterModel parameter)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
// 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.ApplicationModels;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -28,8 +30,8 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
var provider2 = new Mock<IPageApplicationModelProvider>();
|
||||
|
||||
var sequence = 0;
|
||||
var pageApplicationModel1 = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), new object[0]);
|
||||
var pageApplicationModel2 = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), new object[0]);
|
||||
var pageApplicationModel1 = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty<object>());
|
||||
var pageApplicationModel2 = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty<object>());
|
||||
|
||||
provider1.Setup(p => p.OnProvidersExecuting(It.IsAny<PageApplicationModelProviderContext>()))
|
||||
.Callback((PageApplicationModelProviderContext c) =>
|
||||
|
|
@ -103,7 +105,7 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
.Callback((PageApplicationModelProviderContext c) =>
|
||||
{
|
||||
Assert.Equal(1, sequence++);
|
||||
c.PageApplicationModel = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), new object[0]);
|
||||
c.PageApplicationModel = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty<object>());
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
|
|
@ -148,45 +150,346 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void Load_InvokesApplicationModelConventions()
|
||||
public void ApplyConventions_InvokesApplicationModelConventions()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var model = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty<object>());
|
||||
|
||||
var compilerProvider = GetCompilerProvider();
|
||||
|
||||
var model = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), new object[0]);
|
||||
var provider = new Mock<IPageApplicationModelProvider>();
|
||||
provider.Setup(p => p.OnProvidersExecuting(It.IsAny<PageApplicationModelProviderContext>()))
|
||||
.Callback((PageApplicationModelProviderContext c) =>
|
||||
{
|
||||
c.PageApplicationModel = model;
|
||||
});
|
||||
var providers = new[] { provider.Object };
|
||||
|
||||
var razorPagesOptions = Options.Create(new RazorPagesOptions());
|
||||
var mvcOptions = Options.Create(new MvcOptions());
|
||||
var convention = new Mock<IPageApplicationModelConvention>();
|
||||
convention.Setup(c => c.Apply(It.IsAny<PageApplicationModel>()))
|
||||
.Callback((PageApplicationModel m) =>
|
||||
{
|
||||
Assert.Same(model, m);
|
||||
});
|
||||
razorPagesOptions.Value.Conventions.Add(convention.Object);
|
||||
|
||||
var loader = new DefaultPageLoader(
|
||||
providers,
|
||||
compilerProvider,
|
||||
razorPagesOptions,
|
||||
mvcOptions);
|
||||
})
|
||||
.Verifiable();
|
||||
var conventionCollection = new PageConventionCollection
|
||||
{
|
||||
convention.Object,
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = loader.Load(new PageActionDescriptor());
|
||||
DefaultPageLoader.ApplyConventions(conventionCollection, model);
|
||||
|
||||
// Assert
|
||||
convention.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyConventions_InvokesApplicationModelConventions_SpecifiedOnHandlerType()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var handlerConvention = new Mock<IPageApplicationModelConvention>();
|
||||
var model = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), new[] { handlerConvention.Object });
|
||||
|
||||
var globalConvention = new Mock<IPageApplicationModelConvention>();
|
||||
globalConvention.Setup(c => c.Apply(It.IsAny<PageApplicationModel>()))
|
||||
.Callback((PageApplicationModel m) =>
|
||||
{
|
||||
Assert.Same(model, m);
|
||||
})
|
||||
.Verifiable();
|
||||
|
||||
handlerConvention.Setup(c => c.Apply(It.IsAny<PageApplicationModel>()))
|
||||
.Callback((PageApplicationModel m) =>
|
||||
{
|
||||
Assert.Same(model, m);
|
||||
})
|
||||
.Verifiable();
|
||||
var conventionCollection = new PageConventionCollection
|
||||
{
|
||||
globalConvention.Object,
|
||||
};
|
||||
|
||||
// Act
|
||||
DefaultPageLoader.ApplyConventions(conventionCollection, model);
|
||||
|
||||
// Assert
|
||||
globalConvention.Verify();
|
||||
handlerConvention.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyConventions_InvokesHandlerModelConventions()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var methodInfo = GetType().GetMethod(nameof(OnGet), BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
var handlerModelConvention = new Mock<IPageHandlerModelConvention>();
|
||||
|
||||
var applicationModel = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty<object>());
|
||||
var handlerModel = new PageHandlerModel(methodInfo, new[] { handlerModelConvention.Object });
|
||||
|
||||
applicationModel.HandlerMethods.Add(handlerModel);
|
||||
|
||||
handlerModelConvention.Setup(p => p.Apply(It.IsAny<PageHandlerModel>()))
|
||||
.Callback((PageHandlerModel m) =>
|
||||
{
|
||||
Assert.Same(handlerModel, m);
|
||||
})
|
||||
.Verifiable();
|
||||
var conventionCollection = new PageConventionCollection();
|
||||
|
||||
// Act
|
||||
DefaultPageLoader.ApplyConventions(conventionCollection, applicationModel);
|
||||
|
||||
// Assert
|
||||
handlerModelConvention.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyConventions_InvokesHandlerModelConventions_DefinedGlobally()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var methodInfo = GetType().GetMethod(nameof(OnGet), BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
var applicationModel = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty<object>());
|
||||
var handlerModel = new PageHandlerModel(methodInfo, Array.Empty<object>());
|
||||
applicationModel.HandlerMethods.Add(handlerModel);
|
||||
|
||||
var handlerModelConvention = new Mock<IPageHandlerModelConvention>();
|
||||
handlerModelConvention.Setup(p => p.Apply(It.IsAny<PageHandlerModel>()))
|
||||
.Callback((PageHandlerModel m) =>
|
||||
{
|
||||
Assert.Same(handlerModel, m);
|
||||
})
|
||||
.Verifiable();
|
||||
var conventionCollection = new PageConventionCollection { handlerModelConvention.Object };
|
||||
|
||||
// Act
|
||||
DefaultPageLoader.ApplyConventions(conventionCollection, applicationModel);
|
||||
|
||||
// Assert
|
||||
handlerModelConvention.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyConventions_RemovingHandlerAsPartOfHandlerModelConvention_Works()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var methodInfo = GetType().GetMethod(nameof(OnGet), BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
var handlerModelConvention = new Mock<IPageHandlerModelConvention>();
|
||||
|
||||
var applicationModel = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty<object>());
|
||||
var handlerModel = new PageHandlerModel(methodInfo, new[] { handlerModelConvention.Object })
|
||||
{
|
||||
Page = applicationModel,
|
||||
};
|
||||
|
||||
applicationModel.HandlerMethods.Add(handlerModel);
|
||||
|
||||
handlerModelConvention.Setup(p => p.Apply(It.IsAny<PageHandlerModel>()))
|
||||
.Callback((PageHandlerModel m) =>
|
||||
{
|
||||
m.Page.HandlerMethods.Remove(m);
|
||||
})
|
||||
.Verifiable();
|
||||
var conventionCollection = new PageConventionCollection();
|
||||
|
||||
// Act
|
||||
DefaultPageLoader.ApplyConventions(conventionCollection, applicationModel);
|
||||
|
||||
// Assert
|
||||
handlerModelConvention.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyConventions_InvokesParameterModelConventions()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var methodInfo = GetType().GetMethod(nameof(OnGet), BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
var parameterInfo = methodInfo.GetParameters()[0];
|
||||
|
||||
var applicationModel = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty<object>());
|
||||
var handlerModel = new PageHandlerModel(methodInfo, Array.Empty<object>());
|
||||
var parameterModelConvention = new Mock<IParameterModelBaseConvention>();
|
||||
var parameterModel = new PageParameterModel(parameterInfo, new[] { parameterModelConvention.Object });
|
||||
|
||||
applicationModel.HandlerMethods.Add(handlerModel);
|
||||
handlerModel.Parameters.Add(parameterModel);
|
||||
|
||||
parameterModelConvention.Setup(p => p.Apply(It.IsAny<ParameterModelBase>()))
|
||||
.Callback((ParameterModelBase m) =>
|
||||
{
|
||||
Assert.Same(parameterModel, m);
|
||||
})
|
||||
.Verifiable();
|
||||
var conventionCollection = new PageConventionCollection();
|
||||
|
||||
// Act
|
||||
DefaultPageLoader.ApplyConventions(conventionCollection, applicationModel);
|
||||
|
||||
// Assert
|
||||
parameterModelConvention.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyConventions_InvokesParameterModelConventions_DeclaredGlobally()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var methodInfo = GetType().GetMethod(nameof(OnGet), BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
var parameterInfo = methodInfo.GetParameters()[0];
|
||||
|
||||
var applicationModel = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty<object>());
|
||||
var handlerModel = new PageHandlerModel(methodInfo, Array.Empty<object>());
|
||||
var parameterModel = new PageParameterModel(parameterInfo, Array.Empty<object>());
|
||||
|
||||
applicationModel.HandlerMethods.Add(handlerModel);
|
||||
handlerModel.Parameters.Add(parameterModel);
|
||||
|
||||
var parameterModelConvention = new Mock<IParameterModelBaseConvention>();
|
||||
parameterModelConvention.Setup(p => p.Apply(It.IsAny<ParameterModelBase>()))
|
||||
.Callback((ParameterModelBase m) =>
|
||||
{
|
||||
Assert.Same(parameterModel, m);
|
||||
})
|
||||
.Verifiable();
|
||||
var conventionCollection = new PageConventionCollection { parameterModelConvention.Object };
|
||||
|
||||
// Act
|
||||
DefaultPageLoader.ApplyConventions(conventionCollection, applicationModel);
|
||||
|
||||
// Assert
|
||||
parameterModelConvention.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyConventions_RemovingParameterModelAsPartOfConventionWorks()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var methodInfo = GetType().GetMethod(nameof(OnGet), BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
var parameterInfo = methodInfo.GetParameters()[0];
|
||||
|
||||
var applicationModel = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty<object>());
|
||||
var handlerModel = new PageHandlerModel(methodInfo, Array.Empty<object>());
|
||||
var parameterModelConvention = new Mock<IParameterModelBaseConvention>();
|
||||
var parameterModel = new PageParameterModel(parameterInfo, new[] { parameterModelConvention.Object })
|
||||
{
|
||||
Handler = handlerModel,
|
||||
};
|
||||
|
||||
applicationModel.HandlerMethods.Add(handlerModel);
|
||||
handlerModel.Parameters.Add(parameterModel);
|
||||
|
||||
parameterModelConvention.Setup(p => p.Apply(It.IsAny<ParameterModelBase>()))
|
||||
.Callback((ParameterModelBase m) =>
|
||||
{
|
||||
var model = Assert.IsType<PageParameterModel>(m);
|
||||
model.Handler.Parameters.Remove(model);
|
||||
})
|
||||
.Verifiable();
|
||||
var conventionCollection = new PageConventionCollection();
|
||||
|
||||
// Act
|
||||
DefaultPageLoader.ApplyConventions(conventionCollection, applicationModel);
|
||||
|
||||
// Assert
|
||||
parameterModelConvention.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyConventions_InvokesPropertyModelConventions()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var methodInfo = GetType().GetMethod(nameof(OnGet), BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
var propertyInfo = GetType().GetProperty(nameof(TestProperty), BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
var parameterInfo = methodInfo.GetParameters()[0];
|
||||
|
||||
var applicationModel = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty<object>());
|
||||
var handlerModel = new PageHandlerModel(methodInfo, Array.Empty<object>());
|
||||
var parameterModel = new PageParameterModel(parameterInfo, Array.Empty<object>());
|
||||
var propertyModelConvention = new Mock<IParameterModelBaseConvention>();
|
||||
var propertyModel = new PagePropertyModel(propertyInfo, new[] { propertyModelConvention.Object });
|
||||
|
||||
applicationModel.HandlerMethods.Add(handlerModel);
|
||||
applicationModel.HandlerProperties.Add(propertyModel);
|
||||
handlerModel.Parameters.Add(parameterModel);
|
||||
|
||||
propertyModelConvention.Setup(p => p.Apply(It.IsAny<ParameterModelBase>()))
|
||||
.Callback((ParameterModelBase m) =>
|
||||
{
|
||||
Assert.Same(propertyModel, m);
|
||||
})
|
||||
.Verifiable();
|
||||
var conventionCollection = new PageConventionCollection();
|
||||
|
||||
// Act
|
||||
DefaultPageLoader.ApplyConventions(conventionCollection, applicationModel);
|
||||
|
||||
// Assert
|
||||
propertyModelConvention.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyConventions_InvokesPropertyModelConventions_DeclaredGlobally()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var methodInfo = GetType().GetMethod(nameof(OnGet), BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
var propertyInfo = GetType().GetProperty(nameof(TestProperty), BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
var applicationModel = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty<object>());
|
||||
var handlerModel = new PageHandlerModel(methodInfo, Array.Empty<object>());
|
||||
var propertyModel = new PagePropertyModel(propertyInfo, Array.Empty<object>());
|
||||
|
||||
applicationModel.HandlerMethods.Add(handlerModel);
|
||||
applicationModel.HandlerProperties.Add(propertyModel);
|
||||
|
||||
var propertyModelConvention = new Mock<IParameterModelBaseConvention>();
|
||||
propertyModelConvention.Setup(p => p.Apply(It.IsAny<ParameterModelBase>()))
|
||||
.Callback((ParameterModelBase m) =>
|
||||
{
|
||||
Assert.Same(propertyModel, m);
|
||||
})
|
||||
.Verifiable();
|
||||
var conventionCollection = new PageConventionCollection { propertyModelConvention.Object };
|
||||
|
||||
// Act
|
||||
DefaultPageLoader.ApplyConventions(conventionCollection, applicationModel);
|
||||
|
||||
// Assert
|
||||
propertyModelConvention.Verify();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyConventions_RemovingPropertyModelAsPartOfConvention_Works()
|
||||
{
|
||||
// Arrange
|
||||
var descriptor = new PageActionDescriptor();
|
||||
var propertyInfo = GetType().GetProperty(nameof(TestProperty), BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
|
||||
var applicationModel = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty<object>());
|
||||
var propertyModelConvention = new Mock<IParameterModelBaseConvention>();
|
||||
var propertyModel = new PagePropertyModel(propertyInfo, new[] { propertyModelConvention.Object })
|
||||
{
|
||||
Page = applicationModel,
|
||||
};
|
||||
|
||||
applicationModel.HandlerProperties.Add(propertyModel);
|
||||
|
||||
propertyModelConvention.Setup(p => p.Apply(It.IsAny<ParameterModelBase>()))
|
||||
.Callback((ParameterModelBase m) =>
|
||||
{
|
||||
var model = Assert.IsType<PagePropertyModel>(m);
|
||||
model.Page.HandlerProperties.Remove(model);
|
||||
})
|
||||
.Verifiable();
|
||||
var conventionCollection = new PageConventionCollection();
|
||||
|
||||
// Act
|
||||
DefaultPageLoader.ApplyConventions(conventionCollection, applicationModel);
|
||||
|
||||
// Assert
|
||||
propertyModelConvention.Verify();
|
||||
}
|
||||
|
||||
private static IViewCompilerProvider GetCompilerProvider()
|
||||
{
|
||||
var descriptor = new CompiledViewDescriptor
|
||||
|
|
@ -202,5 +505,11 @@ namespace Microsoft.AspNetCore.Mvc.RazorPages.Internal
|
|||
.Returns(compiler.Object);
|
||||
return compilerProvider.Object;
|
||||
}
|
||||
|
||||
private void OnGet(string parameter)
|
||||
{
|
||||
}
|
||||
|
||||
private string TestProperty { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue