Adds common interfaces around Models (Implements #3158)

This commit is contained in:
Nero Sule 2015-09-23 10:03:37 +01:00 committed by Ryan Nowak
parent 9c81b95d1b
commit 49acfd562e
13 changed files with 145 additions and 9 deletions

View File

@ -12,6 +12,30 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </summary> /// </summary>
public class BindingInfo public class BindingInfo
{ {
/// <summary>
/// Creates a new <see cref="BindingInfo"/>.
/// </summary>
public BindingInfo()
{
}
/// <summary>
/// Creates a copy of a <see cref="BindingInfo"/>.
/// </summary>
/// <param name="other">The <see cref="BindingInfo"/> to copy.</param>
public BindingInfo(BindingInfo other)
{
if (other == null)
{
throw new ArgumentNullException(nameof(other));
}
BindingSource = other.BindingSource;
BinderModelName = other.BinderModelName;
BinderType = other.BinderType;
PropertyBindingPredicateProvider = other.PropertyBindingPredicateProvider;
}
/// <summary> /// <summary>
/// Gets or sets the <see cref="ModelBinding.BindingSource"/>. /// Gets or sets the <see cref="ModelBinding.BindingSource"/>.
/// </summary> /// </summary>

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
{ {
[DebuggerDisplay("Name={ActionName}({Methods()}), Type={Controller.ControllerType.Name}," + [DebuggerDisplay("Name={ActionName}({Methods()}), Type={Controller.ControllerType.Name}," +
" Route: {AttributeRouteModel?.Template}, Filters: {Filters.Count}")] " Route: {AttributeRouteModel?.Template}, Filters: {Filters.Count}")]
public class ActionModel public class ActionModel : ICommonModel, IFilterModel, IApiExplorerModel
{ {
public ActionModel( public ActionModel(
[NotNull] MethodInfo actionMethod, [NotNull] MethodInfo actionMethod,
@ -100,6 +100,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
/// </remarks> /// </remarks>
public IDictionary<object, object> Properties { get; } public IDictionary<object, object> Properties { get; }
MemberInfo ICommonModel.MemberInfo => ActionMethod;
string ICommonModel.Name => ActionName;
private string Methods() private string Methods()
{ {
if (HttpMethods.Count == 0) if (HttpMethods.Count == 0)

View File

@ -8,7 +8,7 @@ using Microsoft.AspNet.Mvc.Filters;
namespace Microsoft.AspNet.Mvc.ApplicationModels namespace Microsoft.AspNet.Mvc.ApplicationModels
{ {
[DebuggerDisplay("ApplicationModel: Controllers: {Controllers.Count}, Filters: {Filters.Count}")] [DebuggerDisplay("ApplicationModel: Controllers: {Controllers.Count}, Filters: {Filters.Count}")]
public class ApplicationModel public class ApplicationModel : IPropertyModel, IFilterModel, IApiExplorerModel
{ {
public ApplicationModel() public ApplicationModel()
{ {

View File

@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
{ {
[DebuggerDisplay("Name={ControllerName}, Type={ControllerType.Name}," + [DebuggerDisplay("Name={ControllerName}, Type={ControllerType.Name}," +
" Routes: {AttributeRoutes.Count}, Filters: {Filters.Count}")] " Routes: {AttributeRoutes.Count}, Filters: {Filters.Count}")]
public class ControllerModel public class ControllerModel : ICommonModel, IFilterModel, IApiExplorerModel
{ {
public ControllerModel( public ControllerModel(
[NotNull] TypeInfo controllerType, [NotNull] TypeInfo controllerType,
@ -79,6 +79,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
public IReadOnlyList<object> Attributes { get; } public IReadOnlyList<object> Attributes { get; }
MemberInfo ICommonModel.MemberInfo => ControllerType;
string ICommonModel.Name => ControllerName;
public string ControllerName { get; set; } public string ControllerName { get; set; }
public TypeInfo ControllerType { get; private set; } public TypeInfo ControllerType { get; private set; }

View File

@ -0,0 +1,10 @@
// 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.AspNet.Mvc.ApplicationModels
{
public interface IApiExplorerModel
{
ApiExplorerModel ApiExplorer { get; set; }
}
}

View File

@ -0,0 +1,12 @@
// 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 Microsoft.AspNet.Mvc.ModelBinding;
namespace Microsoft.AspNet.Mvc.ApplicationModels
{
public interface IBindingModel
{
BindingInfo BindingInfo { get; set; }
}
}

View File

@ -0,0 +1,15 @@
// 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.Collections.Generic;
using System.Reflection;
namespace Microsoft.AspNet.Mvc.ApplicationModels
{
public interface ICommonModel : IPropertyModel
{
IReadOnlyList<object> Attributes { get; }
MemberInfo MemberInfo { get; }
string Name { get; }
}
}

View File

@ -0,0 +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.Collections.Generic;
using Microsoft.AspNet.Mvc.Filters;
namespace Microsoft.AspNet.Mvc.ApplicationModels
{
public interface IFilterModel
{
IList<IFilterMetadata> Filters { get; }
}
}

View File

@ -0,0 +1,12 @@
// 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.Collections.Generic;
namespace Microsoft.AspNet.Mvc.ApplicationModels
{
public interface IPropertyModel
{
IDictionary<object, object> Properties { get; }
}
}

View File

@ -10,14 +10,14 @@ using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ApplicationModels namespace Microsoft.AspNet.Mvc.ApplicationModels
{ {
[DebuggerDisplay("ParameterModel: Name={ParameterName}")] [DebuggerDisplay("ParameterModel: Name={ParameterName}")]
public class ParameterModel public class ParameterModel : ICommonModel, IBindingModel
{ {
public ParameterModel( public ParameterModel(
[NotNull] ParameterInfo parameterInfo, [NotNull] ParameterInfo parameterInfo,
[NotNull] IReadOnlyList<object> attributes) [NotNull] IReadOnlyList<object> attributes)
{ {
ParameterInfo = parameterInfo; ParameterInfo = parameterInfo;
Properties = new Dictionary<object, object>();
Attributes = new List<object>(attributes); Attributes = new List<object>(attributes);
} }
@ -25,15 +25,22 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
{ {
Action = other.Action; Action = other.Action;
Attributes = new List<object>(other.Attributes); Attributes = new List<object>(other.Attributes);
BindingInfo = other.BindingInfo; BindingInfo = other.BindingInfo == null ? null : new BindingInfo(other.BindingInfo);
ParameterInfo = other.ParameterInfo; ParameterInfo = other.ParameterInfo;
ParameterName = other.ParameterName; ParameterName = other.ParameterName;
Properties = new Dictionary<object, object>(other.Properties);
} }
public ActionModel Action { get; set; } public ActionModel Action { get; set; }
public IReadOnlyList<object> Attributes { get; } public IReadOnlyList<object> Attributes { get; }
public IDictionary<object, object> Properties { get; }
MemberInfo ICommonModel.MemberInfo => ParameterInfo.Member;
string ICommonModel.Name => ParameterName;
public ParameterInfo ParameterInfo { get; private set; } public ParameterInfo ParameterInfo { get; private set; }
public string ParameterName { get; set; } public string ParameterName { get; set; }

View File

@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
/// A type which is used to represent a property in a <see cref="ControllerModel"/>. /// A type which is used to represent a property in a <see cref="ControllerModel"/>.
/// </summary> /// </summary>
[DebuggerDisplay("PropertyModel: Name={PropertyName}")] [DebuggerDisplay("PropertyModel: Name={PropertyName}")]
public class PropertyModel public class PropertyModel : ICommonModel, IBindingModel
{ {
/// <summary> /// <summary>
/// Creates a new instance of <see cref="PropertyModel"/>. /// Creates a new instance of <see cref="PropertyModel"/>.
@ -25,7 +25,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
[NotNull] IReadOnlyList<object> attributes) [NotNull] IReadOnlyList<object> attributes)
{ {
PropertyInfo = propertyInfo; PropertyInfo = propertyInfo;
Properties = new Dictionary<object, object>();
Attributes = new List<object>(attributes); Attributes = new List<object>(attributes);
} }
@ -37,9 +37,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
{ {
Controller = other.Controller; Controller = other.Controller;
Attributes = new List<object>(other.Attributes); Attributes = new List<object>(other.Attributes);
BindingInfo = other.BindingInfo; BindingInfo = BindingInfo == null ? null : new BindingInfo(other.BindingInfo);
PropertyInfo = other.PropertyInfo; PropertyInfo = other.PropertyInfo;
PropertyName = other.PropertyName; PropertyName = other.PropertyName;
Properties = new Dictionary<object, object>(other.Properties);
} }
/// <summary> /// <summary>
@ -52,6 +53,12 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
/// </summary> /// </summary>
public IReadOnlyList<object> Attributes { get; } public IReadOnlyList<object> Attributes { get; }
public IDictionary<object, object> Properties { get; }
MemberInfo ICommonModel.MemberInfo => PropertyInfo;
string ICommonModel.Name => PropertyName;
/// <summary> /// <summary>
/// Gets or sets the <see cref="BindingInfo"/> associated with this model. /// Gets or sets the <see cref="BindingInfo"/> associated with this model.
/// </summary> /// </summary>

View File

@ -25,6 +25,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
}; };
parameter.ParameterName = "id"; parameter.ParameterName = "id";
parameter.Properties.Add(new KeyValuePair<object, object>("test key", "test value"));
// Act // Act
var parameter2 = new ParameterModel(parameter); var parameter2 = new ParameterModel(parameter);
@ -32,6 +33,12 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
// Assert // Assert
foreach (var property in typeof(ParameterModel).GetProperties()) foreach (var property in typeof(ParameterModel).GetProperties())
{ {
if (property.Name.Equals("BindingInfo"))
{
// This test excludes other mutable objects on purpose because we deep copy them.
continue;
}
var value1 = property.GetValue(parameter); var value1 = property.GetValue(parameter);
var value2 = property.GetValue(parameter2); var value2 = property.GetValue(parameter2);
@ -42,6 +49,13 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
// Ensure non-default value // Ensure non-default value
Assert.NotEmpty((IEnumerable<object>)value1); Assert.NotEmpty((IEnumerable<object>)value1);
} }
else if (typeof(IDictionary<object, object>).IsAssignableFrom(property.PropertyType))
{
Assert.Equal(value1, value2);
// Ensure non-default value
Assert.NotEmpty((IDictionary<object, object>)value1);
}
else if (property.PropertyType.IsValueType || else if (property.PropertyType.IsValueType ||
Nullable.GetUnderlyingType(property.PropertyType) != null) Nullable.GetUnderlyingType(property.PropertyType) != null)
{ {

View File

@ -21,6 +21,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
propertyModel.Controller = new ControllerModel(typeof(TestController).GetTypeInfo(), new List<object>()); propertyModel.Controller = new ControllerModel(typeof(TestController).GetTypeInfo(), new List<object>());
propertyModel.BindingInfo = BindingInfo.GetBindingInfo(propertyModel.Attributes); propertyModel.BindingInfo = BindingInfo.GetBindingInfo(propertyModel.Attributes);
propertyModel.PropertyName = "Property"; propertyModel.PropertyName = "Property";
propertyModel.Properties.Add(new KeyValuePair<object, object>("test key", "test value"));
// Act // Act
var propertyModel2 = new PropertyModel(propertyModel); var propertyModel2 = new PropertyModel(propertyModel);
@ -28,6 +29,12 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
// Assert // Assert
foreach (var property in typeof(PropertyModel).GetProperties()) foreach (var property in typeof(PropertyModel).GetProperties())
{ {
if (property.Name.Equals("BindingInfo"))
{
// This test excludes other mutable objects on purpose because we deep copy them.
continue;
}
var value1 = property.GetValue(propertyModel); var value1 = property.GetValue(propertyModel);
var value2 = property.GetValue(propertyModel2); var value2 = property.GetValue(propertyModel2);
@ -38,6 +45,13 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels
// Ensure non-default value // Ensure non-default value
Assert.NotEmpty((IEnumerable<object>)value1); Assert.NotEmpty((IEnumerable<object>)value1);
} }
else if (typeof(IDictionary<object, object>).IsAssignableFrom(property.PropertyType))
{
Assert.Equal(value1, value2);
// Ensure non-default value
Assert.NotEmpty((IDictionary<object, object>)value1);
}
else if (property.PropertyType.IsValueType || else if (property.PropertyType.IsValueType ||
Nullable.GetUnderlyingType(property.PropertyType) != null) Nullable.GetUnderlyingType(property.PropertyType) != null)
{ {