diff --git a/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/BindingInfo.cs b/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/BindingInfo.cs index ae6c66a2c2..2d1cb1d26f 100644 --- a/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/BindingInfo.cs +++ b/src/Microsoft.AspNet.Mvc.Abstractions/ModelBinding/BindingInfo.cs @@ -12,6 +12,30 @@ namespace Microsoft.AspNet.Mvc.ModelBinding /// public class BindingInfo { + /// + /// Creates a new . + /// + public BindingInfo() + { + } + + /// + /// Creates a copy of a . + /// + /// The to copy. + public BindingInfo(BindingInfo other) + { + if (other == null) + { + throw new ArgumentNullException(nameof(other)); + } + + BindingSource = other.BindingSource; + BinderModelName = other.BinderModelName; + BinderType = other.BinderType; + PropertyBindingPredicateProvider = other.PropertyBindingPredicateProvider; + } + /// /// Gets or sets the . /// diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ActionModel.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ActionModel.cs index 43f4de045e..477cb316ba 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ActionModel.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ActionModel.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels { [DebuggerDisplay("Name={ActionName}({Methods()}), Type={Controller.ControllerType.Name}," + " Route: {AttributeRouteModel?.Template}, Filters: {Filters.Count}")] - public class ActionModel + public class ActionModel : ICommonModel, IFilterModel, IApiExplorerModel { public ActionModel( [NotNull] MethodInfo actionMethod, @@ -100,6 +100,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels /// public IDictionary Properties { get; } + MemberInfo ICommonModel.MemberInfo => ActionMethod; + + string ICommonModel.Name => ActionName; + private string Methods() { if (HttpMethods.Count == 0) diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ApplicationModel.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ApplicationModel.cs index 70e97e6015..4c1bc7232e 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ApplicationModel.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ApplicationModel.cs @@ -8,7 +8,7 @@ using Microsoft.AspNet.Mvc.Filters; namespace Microsoft.AspNet.Mvc.ApplicationModels { [DebuggerDisplay("ApplicationModel: Controllers: {Controllers.Count}, Filters: {Filters.Count}")] - public class ApplicationModel + public class ApplicationModel : IPropertyModel, IFilterModel, IApiExplorerModel { public ApplicationModel() { diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ControllerModel.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ControllerModel.cs index c3ab3486ab..27aa47be8f 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ControllerModel.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ControllerModel.cs @@ -14,7 +14,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels { [DebuggerDisplay("Name={ControllerName}, Type={ControllerType.Name}," + " Routes: {AttributeRoutes.Count}, Filters: {Filters.Count}")] - public class ControllerModel + public class ControllerModel : ICommonModel, IFilterModel, IApiExplorerModel { public ControllerModel( [NotNull] TypeInfo controllerType, @@ -79,6 +79,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels public IReadOnlyList Attributes { get; } + MemberInfo ICommonModel.MemberInfo => ControllerType; + + string ICommonModel.Name => ControllerName; + public string ControllerName { get; set; } public TypeInfo ControllerType { get; private set; } diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IApiExplorerModel.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IApiExplorerModel.cs new file mode 100644 index 0000000000..6e4909ac23 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IApiExplorerModel.cs @@ -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; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IBindingModel.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IBindingModel.cs new file mode 100644 index 0000000000..ce1686092e --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IBindingModel.cs @@ -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; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ICommonModel.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ICommonModel.cs new file mode 100644 index 0000000000..7d0478a5bc --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ICommonModel.cs @@ -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 Attributes { get; } + MemberInfo MemberInfo { get; } + string Name { get; } + } +} diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IFilterModel.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IFilterModel.cs new file mode 100644 index 0000000000..6d00a19077 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IFilterModel.cs @@ -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 Filters { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IPropertyModel.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IPropertyModel.cs new file mode 100644 index 0000000000..c726b9ffb0 --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/IPropertyModel.cs @@ -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 Properties { get; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ParameterModel.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ParameterModel.cs index d4ecdd9e71..afeccf4e58 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ParameterModel.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/ParameterModel.cs @@ -10,14 +10,14 @@ using Microsoft.Framework.Internal; namespace Microsoft.AspNet.Mvc.ApplicationModels { [DebuggerDisplay("ParameterModel: Name={ParameterName}")] - public class ParameterModel + public class ParameterModel : ICommonModel, IBindingModel { public ParameterModel( [NotNull] ParameterInfo parameterInfo, [NotNull] IReadOnlyList attributes) { ParameterInfo = parameterInfo; - + Properties = new Dictionary(); Attributes = new List(attributes); } @@ -25,15 +25,22 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels { Action = other.Action; Attributes = new List(other.Attributes); - BindingInfo = other.BindingInfo; + 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 IDictionary Properties { get; } + + MemberInfo ICommonModel.MemberInfo => ParameterInfo.Member; + + string ICommonModel.Name => ParameterName; + public ParameterInfo ParameterInfo { get; private set; } public string ParameterName { get; set; } diff --git a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/PropertyModel.cs b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/PropertyModel.cs index 20e9b78f50..85f460ae6d 100644 --- a/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/PropertyModel.cs +++ b/src/Microsoft.AspNet.Mvc.Core/ApplicationModels/PropertyModel.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels /// A type which is used to represent a property in a . /// [DebuggerDisplay("PropertyModel: Name={PropertyName}")] - public class PropertyModel + public class PropertyModel : ICommonModel, IBindingModel { /// /// Creates a new instance of . @@ -25,7 +25,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels [NotNull] IReadOnlyList attributes) { PropertyInfo = propertyInfo; - + Properties = new Dictionary(); Attributes = new List(attributes); } @@ -37,9 +37,10 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels { Controller = other.Controller; Attributes = new List(other.Attributes); - BindingInfo = other.BindingInfo; + BindingInfo = BindingInfo == null ? null : new BindingInfo(other.BindingInfo); PropertyInfo = other.PropertyInfo; PropertyName = other.PropertyName; + Properties = new Dictionary(other.Properties); } /// @@ -52,6 +53,12 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels /// public IReadOnlyList Attributes { get; } + public IDictionary Properties { get; } + + MemberInfo ICommonModel.MemberInfo => PropertyInfo; + + string ICommonModel.Name => PropertyName; + /// /// Gets or sets the associated with this model. /// diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/ParameterModelTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/ParameterModelTest.cs index ca666560fd..e41583e814 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/ParameterModelTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/ParameterModelTest.cs @@ -25,6 +25,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels }; parameter.ParameterName = "id"; + parameter.Properties.Add(new KeyValuePair("test key", "test value")); // Act var parameter2 = new ParameterModel(parameter); @@ -32,6 +33,12 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels // Assert 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 value2 = property.GetValue(parameter2); @@ -42,6 +49,13 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels // Ensure non-default value Assert.NotEmpty((IEnumerable)value1); } + else if (typeof(IDictionary).IsAssignableFrom(property.PropertyType)) + { + Assert.Equal(value1, value2); + + // Ensure non-default value + Assert.NotEmpty((IDictionary)value1); + } else if (property.PropertyType.IsValueType || Nullable.GetUnderlyingType(property.PropertyType) != null) { diff --git a/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/PropertyModelTest.cs b/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/PropertyModelTest.cs index 86dcc00ec6..2f92115d57 100644 --- a/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/PropertyModelTest.cs +++ b/test/Microsoft.AspNet.Mvc.Core.Test/ApplicationModel/PropertyModelTest.cs @@ -21,6 +21,7 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels propertyModel.Controller = new ControllerModel(typeof(TestController).GetTypeInfo(), new List()); propertyModel.BindingInfo = BindingInfo.GetBindingInfo(propertyModel.Attributes); propertyModel.PropertyName = "Property"; + propertyModel.Properties.Add(new KeyValuePair("test key", "test value")); // Act var propertyModel2 = new PropertyModel(propertyModel); @@ -28,6 +29,12 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels // Assert 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 value2 = property.GetValue(propertyModel2); @@ -38,6 +45,13 @@ namespace Microsoft.AspNet.Mvc.ApplicationModels // Ensure non-default value Assert.NotEmpty((IEnumerable)value1); } + else if (typeof(IDictionary).IsAssignableFrom(property.PropertyType)) + { + Assert.Equal(value1, value2); + + // Ensure non-default value + Assert.NotEmpty((IDictionary)value1); + } else if (property.PropertyType.IsValueType || Nullable.GetUnderlyingType(property.PropertyType) != null) {