diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/IParameterModelBaseConvention.cs b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/IParameterModelBaseConvention.cs
new file mode 100644
index 0000000000..2457b89f95
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/IParameterModelBaseConvention.cs
@@ -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
+{
+ ///
+ /// Allows customization of the properties and parameters on controllers and Razor Pages.
+ ///
+ ///
+ /// To use this interface, create an class which implements the interface and
+ /// place it on an action method parameter.
+ ///
+ public interface IParameterModelBaseConvention
+ {
+ ///
+ /// Called to apply the convention to the .
+ ///
+ /// The .
+ void Apply(ParameterModelBase parameter);
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/IParameterModelConvention.cs b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/IParameterModelConvention.cs
index 3904bdf73a..eddbbe383c 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/IParameterModelConvention.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/IParameterModelConvention.cs
@@ -4,7 +4,7 @@
namespace Microsoft.AspNetCore.Mvc.ApplicationModels
{
///
- /// Allows customization of the .
+ /// Allows customization of the .
///
///
/// To use this interface, create an class which implements the interface and
@@ -15,10 +15,6 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
///
public interface IParameterModelConvention
{
- ///
- /// Called to apply the convention to the .
- ///
- /// The .
void Apply(ParameterModel parameter);
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ParameterModel.cs b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ParameterModel.cs
index bc1ed07a39..970a026a8a 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ParameterModel.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ParameterModel.cs
@@ -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 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();
- Attributes = new List(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(other.Attributes);
- BindingInfo = other.BindingInfo == null ? null : new BindingInfo(other.BindingInfo);
ParameterInfo = other.ParameterInfo;
- ParameterName = other.ParameterName;
- Properties = new Dictionary(other.Properties);
}
public ActionModel Action { get; set; }
- public IReadOnlyList Attributes { get; }
+ public new IDictionary Properties => base.Properties;
- public IDictionary Properties { get; }
+ public new IReadOnlyList 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;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ParameterModelBase.cs b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ParameterModelBase.cs
new file mode 100644
index 0000000000..7937d8b3c7
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ParameterModelBase.cs
@@ -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
+{
+ ///
+ /// A model type for reading and manipulation properties and parameters.
+ ///
+ /// Derived instances of this type represent properties and parameters for controllers, and Razor Pages.
+ ///
+ ///
+ public abstract class ParameterModelBase : IBindingModel
+ {
+ protected ParameterModelBase(
+ Type parameterType,
+ IReadOnlyList attributes)
+ {
+ ParameterType = parameterType ?? throw new ArgumentNullException(nameof(parameterType));
+ Attributes = new List(attributes ?? throw new ArgumentNullException(nameof(attributes)));
+
+ Properties = new Dictionary();
+ }
+
+ protected ParameterModelBase(ParameterModelBase other)
+ {
+ if (other == null)
+ {
+ throw new ArgumentNullException(nameof(other));
+ }
+
+ ParameterType = other.ParameterType;
+ Attributes = new List(other.Attributes);
+ BindingInfo = other.BindingInfo == null ? null : new BindingInfo(other.BindingInfo);
+ Name = other.Name;
+ Properties = new Dictionary(other.Properties);
+ }
+
+ public IReadOnlyList Attributes { get; }
+
+ public IDictionary Properties { get; }
+
+ public Type ParameterType { get; }
+
+ public string Name { get; protected set; }
+
+ public BindingInfo BindingInfo { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/PropertyModel.cs b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/PropertyModel.cs
index 3db6a8cccc..8f26d0648a 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/PropertyModel.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/PropertyModel.cs
@@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
/// A type which is used to represent a property in a .
///
[DebuggerDisplay("PropertyModel: Name={PropertyName}")]
- public class PropertyModel : ICommonModel, IBindingModel
+ public class PropertyModel : ParameterModelBase, ICommonModel, IBindingModel
{
///
/// Creates a new instance of .
@@ -23,20 +23,9 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
public PropertyModel(
PropertyInfo propertyInfo,
IReadOnlyList 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();
- Attributes = new List(attributes);
+ PropertyInfo = propertyInfo ?? throw new ArgumentNullException(nameof(propertyInfo));
}
///
@@ -44,6 +33,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
///
/// The which needs to be copied.
public PropertyModel(PropertyModel other)
+ : base(other)
{
if (other == null)
{
@@ -51,11 +41,8 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
}
Controller = other.Controller;
- Attributes = new List(other.Attributes);
BindingInfo = BindingInfo == null ? null : new BindingInfo(other.BindingInfo);
PropertyInfo = other.PropertyInfo;
- PropertyName = other.PropertyName;
- Properties = new Dictionary(other.Properties);
}
///
@@ -63,30 +50,18 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
///
public ControllerModel Controller { get; set; }
- ///
- /// Gets any attributes which are annotated on the property.
- ///
- public IReadOnlyList Attributes { get; }
-
- public IDictionary Properties { get; }
-
MemberInfo ICommonModel.MemberInfo => PropertyInfo;
- string ICommonModel.Name => PropertyName;
+ public new IDictionary Properties => base.Properties;
- ///
- /// Gets or sets the associated with this model.
- ///
- public BindingInfo BindingInfo { get; set; }
+ public new IReadOnlyList Attributes => base.Attributes;
- ///
- /// Gets the underlying .
- ///
public PropertyInfo PropertyInfo { get; }
- ///
- /// Gets or sets the name of the property represented by this model.
- ///
- public string PropertyName { get; set; }
+ public string PropertyName
+ {
+ get => Name;
+ set => Name = value;
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/ApplicationModelConventionExtensions.cs b/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/ApplicationModelConventionExtensions.cs
index b23f9d7bed..15f25d474c 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/ApplicationModelConventionExtensions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/DependencyInjection/ApplicationModelConventionExtensions.cs
@@ -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
///
/// The list of s.
/// The type to remove.
- public static void RemoveType(this IList list) where TApplicationModelConvention : IApplicationModelConvention
+ public static void RemoveType(this IList list)
+ where TApplicationModelConvention : IApplicationModelConvention
{
if (list == null)
{
@@ -127,17 +128,36 @@ namespace Microsoft.Extensions.DependencyInjection
conventions.Add(new ParameterApplicationModelConvention(parameterModelConvention));
}
+ ///
+ /// Adds a to all properties and parameters in the application.
+ ///
+ /// The list of
+ /// in .
+ /// The which needs to be
+ /// added.
+ public static void Add(
+ this IList 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;
+ }
+
+ ///
+ 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;
diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ApplicationModelConventions.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ApplicationModelConventions.cs
index d4662940d9..108890f5ce 100644
--- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ApplicationModelConventions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ApplicationModelConventions.cs
@@ -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(conventions, parameter.Attributes);
+ foreach (var parameterConvention in parameterBaseConventions)
+ {
+ parameterConvention.Apply(parameter);
+ }
+ }
+ }
+
+ var properties = controller.ControllerProperties.ToArray();
+ foreach (var property in properties)
+ {
+ var parameterBaseConventions = GetConventions(conventions, property.Attributes);
+
+ foreach (var parameterConvention in parameterBaseConventions)
+ {
+ parameterConvention.Apply(property);
}
}
}
}
+
+ private static IEnumerable GetConventions(
+ IEnumerable conventions,
+ IReadOnlyList attributes)
+ {
+ return Enumerable.Concat(
+ conventions.OfType(),
+ attributes.OfType());
+ }
}
-}
\ No newline at end of file
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageHandlerModelConvention.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageHandlerModelConvention.cs
new file mode 100644
index 0000000000..df2b0c7a69
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/IPageHandlerModelConvention.cs
@@ -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
+{
+ ///
+ /// Allows customization of the .
+ ///
+ public interface IPageHandlerModelConvention : IPageConvention
+ {
+ ///
+ /// Called to apply the convention to the .
+ ///
+ /// The .
+ void Apply(PageHandlerModel model);
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/PageParameterModel.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/PageParameterModel.cs
index c4547411c0..0b4851b1ee 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/PageParameterModel.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/PageParameterModel.cs
@@ -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 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();
- Attributes = new List(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(other.Attributes);
- BindingInfo = other.BindingInfo == null ? null : new BindingInfo(other.BindingInfo);
ParameterInfo = other.ParameterInfo;
- ParameterName = other.ParameterName;
- Properties = new Dictionary(other.Properties);
}
public PageHandlerModel Handler { get; set; }
- public IReadOnlyList Attributes { get; }
-
- public IDictionary 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;
+ }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/PagePropertyModel.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/PagePropertyModel.cs
index f76d7da366..5af225dee6 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/PagePropertyModel.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/ApplicationModels/PagePropertyModel.cs
@@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
/// Represents a property in a .
///
[DebuggerDisplay("PagePropertyModel: Name={PropertyName}")]
- public class PagePropertyModel : ICommonModel, IBindingModel
+ public class PagePropertyModel : ParameterModelBase, ICommonModel
{
///
/// Creates a new instance of .
@@ -23,10 +23,9 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
public PagePropertyModel(
PropertyInfo propertyInfo,
IReadOnlyList attributes)
+ : base(propertyInfo?.PropertyType, attributes)
{
PropertyInfo = propertyInfo ?? throw new ArgumentNullException(nameof(propertyInfo));
- Properties = new Dictionary();
- Attributes = new List(attributes) ?? throw new ArgumentNullException(nameof(attributes));
}
///
@@ -34,6 +33,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
///
/// The which needs to be copied.
public PagePropertyModel(PagePropertyModel other)
+ : base(other)
{
if (other == null)
{
@@ -41,11 +41,8 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
}
Page = other.Page;
- Attributes = new List(other.Attributes);
BindingInfo = BindingInfo == null ? null : new BindingInfo(other.BindingInfo);
PropertyInfo = other.PropertyInfo;
- PropertyName = other.PropertyName;
- Properties = new Dictionary(other.Properties);
}
///
@@ -53,31 +50,14 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels
///
public PageApplicationModel Page { get; set; }
- ///
- /// Gets any attributes which are annotated on the property.
- ///
- public IReadOnlyList Attributes { get; }
-
- ///
- public IDictionary Properties { get; }
-
- ///
- /// Gets or sets the associated with this model.
- ///
- public BindingInfo BindingInfo { get; set; }
-
- ///
- /// Gets the underlying .
- ///
- public PropertyInfo PropertyInfo { get; }
-
- ///
- /// Gets or sets the name of the property represented by this model.
- ///
- 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;
+ }
}
}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/PageConventionCollectionExtensions.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/PageConventionCollectionExtensions.cs
index 6daa490f9f..577c8b5ed3 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/PageConventionCollectionExtensions.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/DependencyInjection/PageConventionCollectionExtensions.cs
@@ -143,6 +143,30 @@ namespace Microsoft.Extensions.DependencyInjection
return conventions;
}
+ ///
+ /// Adds the specified to .
+ /// The added convention will apply to all handler properties and parameters on handler methods.
+ ///
+ /// The to configure.
+ /// The to apply.
+ /// The .
+ 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;
+ }
+
///
/// Adds a to all pages under the specified area folder.
///
@@ -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);
+ }
+ }
}
}
diff --git a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs
index e31ed6d32b..23a57a786e 100644
--- a/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs
+++ b/src/Microsoft.AspNetCore.Mvc.RazorPages/Internal/DefaultPageLoader.cs
@@ -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()
- .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(pageApplicationModel.HandlerTypeAttributes);
+ foreach (var convention in applicationModelConventions)
+ {
+ convention.Apply(pageApplicationModel);
+ }
+
+ var handlers = pageApplicationModel.HandlerMethods.ToArray();
+ foreach (var handlerModel in handlers)
+ {
+ var handlerModelConventions = GetConventions(handlerModel.Attributes);
+ foreach (var convention in handlerModelConventions)
+ {
+ convention.Apply(handlerModel);
+ }
+
+ var parameterModels = handlerModel.Parameters.ToArray();
+ foreach (var parameterModel in parameterModels)
+ {
+ var parameterModelConventions = GetConventions(parameterModel.Attributes);
+ foreach (var convention in parameterModelConventions)
+ {
+ convention.Apply(parameterModel);
+ }
+ }
+ }
+
+ var properties = pageApplicationModel.HandlerProperties.ToArray();
+ foreach (var propertyModel in properties)
+ {
+ var propertyModelConventions = GetConventions(propertyModel.Attributes);
+ foreach (var convention in propertyModelConventions)
+ {
+ convention.Apply(propertyModel);
+ }
+ }
+
+ IEnumerable GetConventions(
+ IReadOnlyList attributes)
+ {
+ return Enumerable.Concat(
+ conventions.OfType(),
+ attributes.OfType());
+ }
+ }
}
}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/DependencyInjection/ApplicationModelConventionExtensionsTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/DependencyInjection/ApplicationModelConventionExtensionsTest.cs
index 134fdfb688..51811324e8 100644
--- a/test/Microsoft.AspNetCore.Mvc.Core.Test/DependencyInjection/ApplicationModelConventionExtensionsTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/DependencyInjection/ApplicationModelConventionExtensionsTest.cs
@@ -18,8 +18,16 @@ namespace Microsoft.Extensions.DependencyInjection
{
// Arrange
var app = new ApplicationModel();
- app.Controllers.Add(new ControllerModel(typeof(HelloController).GetTypeInfo(), new List()));
- app.Controllers.Add(new ControllerModel(typeof(WorldController).GetTypeInfo(), new List()));
+ var controllerType = typeof(HelloController);
+ var controllerModel = new ControllerModel(controllerType.GetTypeInfo(), Array.Empty());
+ app.Controllers.Add(controllerModel);
+
+ var actionModel = new ActionModel(controllerType.GetMethod(nameof(HelloController.GetInfo)), Array.Empty());
+ controllerModel.Actions.Add(actionModel);
+ var parameterModel = new ParameterModel(
+ controllerType.GetMethod(nameof(HelloController.GetInfo)).GetParameters()[0],
+ Array.Empty());
+ 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()));
- app.Controllers.Add(new ControllerModel(typeof(WorldController).GetTypeInfo(), new List()));
+ var controllerType1 = typeof(HelloController).GetTypeInfo();
+ var actionMethod1 = controllerType1.GetMethod(nameof(HelloController.GetHello));
+ var controllerModel1 = new ControllerModel(controllerType1, Array.Empty())
+ {
+ Actions =
+ {
+ new ActionModel(actionMethod1, Array.Empty()),
+ }
+ };
+
+ var controllerType2 = typeof(WorldController).GetTypeInfo();
+ var actionMethod2 = controllerType2.GetMethod(nameof(WorldController.GetWorld));
+ var controllerModel2 = new ControllerModel(controllerType2, Array.Empty())
+ {
+ Actions =
+ {
+ new ActionModel(actionMethod2, Array.Empty()),
+ },
+ };
+
+ 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());
+ var actionMethod1 = controllerType1.GetMethod(nameof(HelloController.GetInfo));
+ var property1 = controllerType1.GetProperty(nameof(HelloController.Property1));
+ var controllerModel1 = new ControllerModel(controllerType1, Array.Empty())
{
- 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()),
+ },
+ Actions =
+ {
+ new ActionModel(actionMethod1, Array.Empty())
+ {
+ Parameters =
+ {
+ parameterModel1,
+ }
+ }
}
- }
+ };
+
+ var controllerType2 = typeof(WorldController).GetTypeInfo();
+ var property2 = controllerType2.GetProperty(nameof(WorldController.Property2));
+ var controllerModel2 = new ControllerModel(controllerType2, Array.Empty())
+ {
+ ControllerProperties =
+ {
+ new PropertyModel(property2, Array.Empty()),
+ },
+ };
+
+ 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()));
- app.Controllers.Add(new ControllerModel(typeof(WorldController).GetTypeInfo(), new List()));
+ app.Controllers.Add(new ControllerModel(typeof(HelloController).GetTypeInfo(), Array.Empty()));
+ app.Controllers.Add(new ControllerModel(typeof(WorldController).GetTypeInfo(), Array.Empty()));
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())
+ new ControllerModel(typeof(HelloController).GetTypeInfo(), Array.Empty())
{
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();
+
+ // 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())
+ var controllerModel = new ControllerModel(controllerType, Array.Empty())
{
Application = applicationModel
};
controllerModel.Actions.Add(
- new ActionModel(controllerType.GetMethod(nameof(HelloController.GetHello)), new List())
+ new ActionModel(controllerType.GetMethod(nameof(HelloController.GetHello)), Array.Empty())
{
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())
+ {
+ Application = applicationModel
+ };
+ controllerModel.ControllerProperties.Add(
+ new PropertyModel(controllerType.GetProperty(nameof(HelloController.Property1)), Array.Empty())
+ {
+ Controller = controllerModel
+ });
+ applicationModel.Controllers.Add(controllerModel);
+
+ var propertyModelConvention = new ParameterModelBaseConvention();
+ var conventions = new List();
+ 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())
+ {
+ Application = applicationModel
+ };
+ controllerModel.ControllerProperties.Add(
+ new PropertyModel(controllerType.GetProperty(nameof(HelloController.Property1)), new[] { propertyModelConvention })
+ {
+ Controller = controllerModel
+ });
+ applicationModel.Controllers.Add(controllerModel);
+
+ var conventions = new List();
+
+ // 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())
+ var controllerModel = new ControllerModel(controllerType, Array.Empty())
{
Application = app
};
app.Controllers.Add(controllerModel);
- var actionModel = new ActionModel(controllerType.GetMethod(nameof(HelloController.GetInfo)), new List())
+ var actionModel = new ActionModel(controllerType.GetMethod(nameof(HelloController.GetInfo)), Array.Empty())
{
Controller = controllerModel
};
controllerModel.Actions.Add(actionModel);
var parameterModel = new ParameterModel(
controllerType.GetMethod(nameof(HelloController.GetInfo)).GetParameters()[0],
- new List())
+ Array.Empty())
{
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())
+ {
+ Application = app
+ };
+ app.Controllers.Add(controllerModel);
+ var actionModel = new ActionModel(controllerType.GetMethod(nameof(HelloController.GetInfo)), Array.Empty())
+ {
+ 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();
+
+ // 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)
diff --git a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageLoaderTest.cs b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageLoaderTest.cs
index 169a0db20f..d31359df2f 100644
--- a/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageLoaderTest.cs
+++ b/test/Microsoft.AspNetCore.Mvc.RazorPages.Test/Internal/DefaultPageLoaderTest.cs
@@ -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();
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());
+ var pageApplicationModel2 = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty());
provider1.Setup(p => p.OnProvidersExecuting(It.IsAny()))
.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());
})
.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());
- var compilerProvider = GetCompilerProvider();
-
- var model = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), new object[0]);
- var provider = new Mock();
- provider.Setup(p => p.OnProvidersExecuting(It.IsAny()))
- .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();
convention.Setup(c => c.Apply(It.IsAny()))
.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();
+ var model = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), new[] { handlerConvention.Object });
+
+ var globalConvention = new Mock();
+ globalConvention.Setup(c => c.Apply(It.IsAny()))
+ .Callback((PageApplicationModel m) =>
+ {
+ Assert.Same(model, m);
+ })
+ .Verifiable();
+
+ handlerConvention.Setup(c => c.Apply(It.IsAny()))
+ .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();
+
+ var applicationModel = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty());
+ var handlerModel = new PageHandlerModel(methodInfo, new[] { handlerModelConvention.Object });
+
+ applicationModel.HandlerMethods.Add(handlerModel);
+
+ handlerModelConvention.Setup(p => p.Apply(It.IsAny()))
+ .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());
+ var handlerModel = new PageHandlerModel(methodInfo, Array.Empty());
+ applicationModel.HandlerMethods.Add(handlerModel);
+
+ var handlerModelConvention = new Mock();
+ handlerModelConvention.Setup(p => p.Apply(It.IsAny()))
+ .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();
+
+ var applicationModel = new PageApplicationModel(descriptor, typeof(object).GetTypeInfo(), Array.Empty());
+ var handlerModel = new PageHandlerModel(methodInfo, new[] { handlerModelConvention.Object })
+ {
+ Page = applicationModel,
+ };
+
+ applicationModel.HandlerMethods.Add(handlerModel);
+
+ handlerModelConvention.Setup(p => p.Apply(It.IsAny()))
+ .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());
+ var handlerModel = new PageHandlerModel(methodInfo, Array.Empty());
+ var parameterModelConvention = new Mock();
+ var parameterModel = new PageParameterModel(parameterInfo, new[] { parameterModelConvention.Object });
+
+ applicationModel.HandlerMethods.Add(handlerModel);
+ handlerModel.Parameters.Add(parameterModel);
+
+ parameterModelConvention.Setup(p => p.Apply(It.IsAny()))
+ .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());
+ var handlerModel = new PageHandlerModel(methodInfo, Array.Empty());
+ var parameterModel = new PageParameterModel(parameterInfo, Array.Empty());
+
+ applicationModel.HandlerMethods.Add(handlerModel);
+ handlerModel.Parameters.Add(parameterModel);
+
+ var parameterModelConvention = new Mock();
+ parameterModelConvention.Setup(p => p.Apply(It.IsAny()))
+ .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());
+ var handlerModel = new PageHandlerModel(methodInfo, Array.Empty());
+ var parameterModelConvention = new Mock();
+ 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()))
+ .Callback((ParameterModelBase m) =>
+ {
+ var model = Assert.IsType(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());
+ var handlerModel = new PageHandlerModel(methodInfo, Array.Empty());
+ var parameterModel = new PageParameterModel(parameterInfo, Array.Empty());
+ var propertyModelConvention = new Mock();
+ 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()))
+ .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());
+ var handlerModel = new PageHandlerModel(methodInfo, Array.Empty());
+ var propertyModel = new PagePropertyModel(propertyInfo, Array.Empty());
+
+ applicationModel.HandlerMethods.Add(handlerModel);
+ applicationModel.HandlerProperties.Add(propertyModel);
+
+ var propertyModelConvention = new Mock();
+ propertyModelConvention.Setup(p => p.Apply(It.IsAny()))
+ .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());
+ var propertyModelConvention = new Mock();
+ var propertyModel = new PagePropertyModel(propertyInfo, new[] { propertyModelConvention.Object })
+ {
+ Page = applicationModel,
+ };
+
+ applicationModel.HandlerProperties.Add(propertyModel);
+
+ propertyModelConvention.Setup(p => p.Apply(It.IsAny()))
+ .Callback((ParameterModelBase m) =>
+ {
+ var model = Assert.IsType(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; }
}
}