Add more details to ModelAttributes

This change adds more information to ModelAttributes, so that metadata
providers can look at the attributes on the property and type separately
if so desired
This commit is contained in:
Ryan Nowak 2015-04-16 16:25:25 -07:00
parent a4fd51772f
commit 2719111453
18 changed files with 285 additions and 133 deletions

View File

@ -18,10 +18,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
/// <param name="attributes">The attributes for the <see cref="ModelMetadata"/>.</param>
public BindingMetadataProviderContext(
[NotNull] ModelMetadataIdentity key,
[NotNull] IReadOnlyList<object> attributes)
[NotNull] ModelAttributes attributes)
{
Key = key;
Attributes = attributes;
Attributes = attributes.Attributes;
PropertyAttributes = attributes.PropertyAttributes;
TypeAttributes = attributes.TypeAttributes;
BindingMetadata = new BindingMetadata();
}
@ -35,6 +38,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
/// </summary>
public ModelMetadataIdentity Key { get; }
/// <summary>
/// Gets the property attributes.
/// </summary>
public IReadOnlyList<object> PropertyAttributes { get; }
/// <summary>
/// Gets the type attributes.
/// </summary>
public IReadOnlyList<object> TypeAttributes { get; }
/// <summary>
/// Gets the <see cref="Metadata.BindingMetadata"/>.
/// </summary>

View File

@ -29,7 +29,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
}
var dataMemberAttribute = context
.Attributes
.PropertyAttributes
.OfType<DataMemberAttribute>()
.FirstOrDefault();
if (dataMemberAttribute == null || !dataMemberAttribute.IsRequired)

View File

@ -2,7 +2,7 @@
// 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.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
@ -19,16 +19,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
/// </summary>
/// <param name="key">The <see cref="ModelMetadataIdentity"/>.</param>
/// <param name="attributes">The set of model attributes.</param>
public DefaultMetadataDetails(ModelMetadataIdentity key, IReadOnlyList<object> attributes)
public DefaultMetadataDetails(ModelMetadataIdentity key, [NotNull] ModelAttributes attributes)
{
Key = key;
Attributes = attributes;
ModelAttributes = attributes;
}
/// <summary>
/// Gets or sets the set of model attributes.
/// </summary>
public IReadOnlyList<object> Attributes { get; }
public ModelAttributes ModelAttributes { get; }
/// <summary>
/// Gets or sets the <see cref="Metadata.BindingMetadata"/>.

View File

@ -44,11 +44,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
/// <summary>
/// Gets the set of attributes for the current instance.
/// </summary>
public IReadOnlyList<object> Attributes
public ModelAttributes Attributes
{
get
{
return _details.Attributes;
return _details.ModelAttributes;
}
}
@ -64,7 +64,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
if (_details.BindingMetadata == null)
{
var context = new BindingMetadataProviderContext(Identity, _details.Attributes);
var context = new BindingMetadataProviderContext(Identity, _details.ModelAttributes);
_detailsProvider.GetBindingMetadata(context);
_details.BindingMetadata = context.BindingMetadata;
}
@ -85,7 +85,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
if (_details.DisplayMetadata == null)
{
var context = new DisplayMetadataProviderContext(Identity, _details.Attributes);
var context = new DisplayMetadataProviderContext(Identity, _details.ModelAttributes);
_detailsProvider.GetDisplayMetadata(context);
_details.DisplayMetadata = context.DisplayMetadata;
}
@ -106,7 +106,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
if (_details.ValidationMetadata == null)
{
var context = new ValidationMetadataProviderContext(Identity, _details.Attributes);
var context = new ValidationMetadataProviderContext(Identity, _details.ModelAttributes);
_detailsProvider.GetValidationMetadata(context);
_details.ValidationMetadata = context.ValidationMetadata;
}

View File

@ -114,9 +114,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
propertyHelper.Name,
key.ModelType);
var attributes = new List<object>(ModelAttributes.GetAttributesForProperty(
var attributes = ModelAttributes.GetAttributesForProperty(
key.ModelType,
propertyHelper.Property));
propertyHelper.Property);
var propertyEntry = new DefaultMetadataDetails(propertyKey, attributes);
if (propertyHelper.Property.CanRead && propertyHelper.Property.GetMethod?.IsPublic == true)
@ -151,8 +151,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
/// </remarks>
protected virtual DefaultMetadataDetails CreateTypeDetails([NotNull] ModelMetadataIdentity key)
{
var attributes = new List<object>(ModelAttributes.GetAttributesForType(key.ModelType));
return new DefaultMetadataDetails(key, attributes);
return new DefaultMetadataDetails(key, ModelAttributes.GetAttributesForType(key.ModelType));
}
private class TypeCache : ConcurrentDictionary<ModelMetadataIdentity, ModelMetadataCacheEntry>

View File

@ -18,10 +18,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
/// <param name="attributes">The attributes for the <see cref="ModelMetadata"/>.</param>
public DisplayMetadataProviderContext(
[NotNull] ModelMetadataIdentity key,
[NotNull] IReadOnlyList<object> attributes)
[NotNull] ModelAttributes attributes)
{
Key = key;
Attributes = attributes;
Attributes = attributes.Attributes;
PropertyAttributes = attributes.PropertyAttributes;
TypeAttributes = attributes.TypeAttributes;
DisplayMetadata = new DisplayMetadata();
}
@ -30,14 +33,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
/// </summary>
public IReadOnlyList<object> Attributes { get; }
/// <summary>
/// Gets the <see cref="Metadata.DisplayMetadata"/>.
/// </summary>
public DisplayMetadata DisplayMetadata { get; }
/// <summary>
/// Gets the <see cref="ModelMetadataIdentity"/>.
/// </summary>
public ModelMetadataIdentity Key { get; }
/// <summary>
/// Gets the <see cref="Metadata.DisplayMetadata"/>.
/// Gets the property attributes.
/// </summary>
public DisplayMetadata DisplayMetadata { get; }
public IReadOnlyList<object> PropertyAttributes { get; }
/// <summary>
/// Gets the type attributes.
/// </summary>
public IReadOnlyList<object> TypeAttributes { get; }
}
}

View File

@ -10,27 +10,53 @@ using Microsoft.Framework.Internal;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
/// <summary>
/// Provides static methods which can be used to get a combined list of attributes associated
/// with a parameter or property.
/// Provides access to the combined list of attributes associated a <see cref="Type"/> or property.
/// </summary>
public static class ModelAttributes
public class ModelAttributes
{
/// <summary>
/// Gets the attributes for the given <paramref name="parameter"/>.
/// Creates a new <see cref="ModelAttributes"/> for a <see cref="Type"/>.
/// </summary>
/// <param name="parameter">A <see cref="ParameterInfo"/> for which attributes need to be resolved.
/// </param>
/// <returns>An <see cref="IEnumerable{object}"/> containing the attributes on the
/// <paramref name="parameter"/> before the attributes on the <paramref name="parameter"/> type.</returns>
public static IEnumerable<object> GetAttributesForParameter(ParameterInfo parameter)
/// <param name="typeAttributes">The set of attributes for the <see cref="Type"/>.</param>
public ModelAttributes([NotNull] IEnumerable<object> typeAttributes)
{
// Return the parameter attributes first.
var parameterAttributes = parameter.GetCustomAttributes();
var typeAttributes = parameter.ParameterType.GetTypeInfo().GetCustomAttributes();
return parameterAttributes.Concat(typeAttributes);
Attributes = typeAttributes.ToArray();
TypeAttributes = Attributes;
}
/// <summary>
/// Creates a new <see cref="ModelAttributes"/> for a property.
/// </summary>
/// <param name="propertyAttributes">The set of attributes for the property.</param>
/// <param name="typeAttributes">
/// The set of attributes for the property's <see cref="Type"/>. See <see cref="PropertyInfo.PropertyType"/>.
/// </param>
public ModelAttributes([NotNull] IEnumerable<object> propertyAttributes, [NotNull] IEnumerable<object> typeAttributes)
{
PropertyAttributes = propertyAttributes.ToArray();
TypeAttributes = typeAttributes.ToArray();
Attributes = PropertyAttributes.Concat(TypeAttributes).ToArray();
}
/// <summary>
/// Gets the set of all attributes. If this instance represents the attributes for a property, the attributes
/// on the property definition are before those on the property's <see cref="Type"/>.
/// </summary>
public IReadOnlyList<object> Attributes { get; }
/// <summary>
/// Gets the set of attributes on the property, or <c>null</c> if this instance represents the attributes
/// for a <see cref="Type"/>.
/// </summary>
public IReadOnlyList<object> PropertyAttributes { get; }
/// <summary>
/// Gets the set of attributes on the <see cref="Type"/>. If this instance represents a property,
/// then <see cref="TypeAttributes"/> contains attributes retrieved from
/// <see cref="PropertyInfo.PropertyType"/>.
/// </summary>
public IReadOnlyList<object> TypeAttributes { get; }
/// <summary>
/// Gets the attributes for the given <paramref name="property"/>.
/// </summary>
@ -38,32 +64,26 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </param>
/// <param name="property">A <see cref="PropertyInfo"/> for which attributes need to be resolved.
/// </param>
/// <returns>An <see cref="IEnumerable{object}"/> containing the attributes on the
/// <paramref name="property"/> before the attributes on the <paramref name="property"/> type.</returns>
public static IEnumerable<object> GetAttributesForProperty([NotNull] Type type, [NotNull] PropertyInfo property)
/// <returns>A <see cref="ModelAttributes"/> instance with the attributes of the property.</returns>
public static ModelAttributes GetAttributesForProperty([NotNull] Type type, [NotNull] PropertyInfo property)
{
// Return the property attributes first.
var propertyAttributes = property.GetCustomAttributes();
var typeAttributes = property.PropertyType.GetTypeInfo().GetCustomAttributes();
propertyAttributes = propertyAttributes.Concat(typeAttributes);
var modelMedatadataType = type.GetTypeInfo().GetCustomAttribute<ModelMetadataTypeAttribute>();
if (modelMedatadataType != null)
var metadataType = GetMetadataType(type);
if (metadataType != null)
{
var modelMedatadataProperty = modelMedatadataType.MetadataType.GetRuntimeProperty(property.Name);
if (modelMedatadataProperty != null)
var metadataProperty = metadataType.GetRuntimeProperty(property.Name);
if (metadataProperty != null)
{
var modelMedatadataAttributes = modelMedatadataProperty.GetCustomAttributes();
propertyAttributes = propertyAttributes.Concat(modelMedatadataAttributes);
propertyAttributes = propertyAttributes.Concat(metadataProperty.GetCustomAttributes());
var modelMetadataTypeAttributes =
modelMedatadataProperty.PropertyType.GetTypeInfo().GetCustomAttributes();
propertyAttributes = propertyAttributes.Concat(modelMetadataTypeAttributes);
var propertyMetadataType = metadataProperty.PropertyType;
typeAttributes = typeAttributes.Concat(propertyMetadataType.GetTypeInfo().GetCustomAttributes());
}
}
return propertyAttributes;
return new ModelAttributes(propertyAttributes, typeAttributes);
}
/// <summary>
@ -71,20 +91,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
/// </summary>
/// <param name="type">The <see cref="Type"/> for which attributes need to be resolved.
/// </param>
/// <returns>An <see cref="IEnumerable{object}"/> containing the attributes on the
/// <paramref name="type"/>.</returns>
public static IEnumerable<object> GetAttributesForType([NotNull] Type type)
/// <returns>A <see cref="ModelAttributes"/> instance with the attributes of the <see cref="Type"/>.</returns>
public static ModelAttributes GetAttributesForType([NotNull] Type type)
{
var attributes = type.GetTypeInfo().GetCustomAttributes();
var modelMedatadataType = type.GetTypeInfo().GetCustomAttribute<ModelMetadataTypeAttribute>();
if (modelMedatadataType != null)
var metadataType = GetMetadataType(type);
if (metadataType != null)
{
var modelMedatadataAttributes = modelMedatadataType.MetadataType.GetTypeInfo().GetCustomAttributes();
attributes = attributes.Concat(modelMedatadataAttributes);
attributes = attributes.Concat(metadataType.GetTypeInfo().GetCustomAttributes());
}
return attributes;
return new ModelAttributes(attributes);
}
private static Type GetMetadataType(Type type)
{
return type.GetTypeInfo().GetCustomAttribute<ModelMetadataTypeAttribute>()?.MetadataType;
}
}
}

View File

@ -18,10 +18,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
/// <param name="attributes">The attributes for the <see cref="ModelMetadata"/>.</param>
public ValidationMetadataProviderContext(
[NotNull] ModelMetadataIdentity key,
[NotNull] IReadOnlyList<object> attributes)
[NotNull] ModelAttributes attributes)
{
Key = key;
Attributes = attributes;
Attributes = attributes.Attributes;
PropertyAttributes = attributes.PropertyAttributes;
TypeAttributes = attributes.TypeAttributes;
ValidationMetadata = new ValidationMetadata();
}
@ -35,6 +38,16 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
/// </summary>
public ModelMetadataIdentity Key { get; }
/// <summary>
/// Gets the property attributes.
/// </summary>
public IReadOnlyList<object> PropertyAttributes { get; }
/// <summary>
/// Gets the type attributes.
/// </summary>
public IReadOnlyList<object> TypeAttributes { get; }
/// <summary>
/// Gets the <see cref="Metadata.ValidationMetadata"/>.
/// </summary>

View File

@ -13,5 +13,8 @@
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -57,7 +57,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var provider = new DataAnnotationsMetadataProvider();
var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, new object[] { attribute });
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(new object[] { attribute }));
// Act
provider.GetDisplayMetadata(context);
@ -78,7 +78,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var attributes = new[] { dataType, };
var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, attributes);
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes));
// Act
provider.GetDisplayMetadata(context);
@ -101,7 +101,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var attributes = new Attribute[] { dataType, displayFormat, };
var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, attributes);
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes));
// Act
provider.GetDisplayMetadata(context);
@ -120,7 +120,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var attributes = new Attribute[] { editable };
var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new BindingMetadataProviderContext(key, attributes);
var context = new BindingMetadataProviderContext(key, new ModelAttributes(attributes));
// Act
provider.GetBindingMetadata(context);
@ -139,7 +139,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var attributes = new Attribute[] { editable };
var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new BindingMetadataProviderContext(key, attributes);
var context = new BindingMetadataProviderContext(key, new ModelAttributes(attributes));
// Act
provider.GetBindingMetadata(context);
@ -169,7 +169,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var attributes = new Attribute[] { display };
var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, attributes);
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes));
// Act
provider.GetDisplayMetadata(context);
@ -198,7 +198,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var attributes = new Attribute[] { display };
var key = ModelMetadataIdentity.ForType(typeof(string));
var context = new DisplayMetadataProviderContext(key, attributes);
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes));
// Act
provider.GetDisplayMetadata(context);
@ -232,7 +232,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var key = ModelMetadataIdentity.ForType(type);
var attributes = new object[0];
var context = new DisplayMetadataProviderContext(key, attributes);
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes));
// Act
provider.GetDisplayMetadata(context);
@ -266,7 +266,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var key = ModelMetadataIdentity.ForType(type);
var attributes = new object[0];
var context = new DisplayMetadataProviderContext(key, attributes);
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes));
// Act
provider.GetDisplayMetadata(context);
@ -398,7 +398,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var key = ModelMetadataIdentity.ForType(type);
var attributes = new object[0];
var context = new DisplayMetadataProviderContext(key, attributes);
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes));
// Act
provider.GetDisplayMetadata(context);
@ -532,7 +532,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var key = ModelMetadataIdentity.ForType(type);
var attributes = new object[0];
var context = new DisplayMetadataProviderContext(key, attributes);
var context = new DisplayMetadataProviderContext(key, new ModelAttributes(attributes));
// Act
provider.GetDisplayMetadata(context);
@ -551,7 +551,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var attributes = new Attribute[] { required };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var context = new ValidationMetadataProviderContext(key, attributes);
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(attributes, new object[0]));
// Act
provider.GetValidationMetadata(context);
@ -571,7 +571,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var attributes = new Attribute[] { };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var context = new ValidationMetadataProviderContext(key, attributes);
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(attributes, new object[0]));
context.ValidationMetadata.IsRequired = initialValue;
// Act
@ -592,7 +592,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var attributes = new Attribute[] { new RequiredAttribute() };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var context = new BindingMetadataProviderContext(key, attributes);
var context = new BindingMetadataProviderContext(key, new ModelAttributes(attributes, new object[0]));
context.BindingMetadata.IsBindingRequired = initialValue;
// Act
@ -613,7 +613,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var attributes = new Attribute[] { };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var context = new BindingMetadataProviderContext(key, attributes);
var context = new BindingMetadataProviderContext(key, new ModelAttributes(attributes, new object[0]));
context.BindingMetadata.IsReadOnly = initialValue;
// Act
@ -632,7 +632,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var attribute = new TestValidationAttribute();
var attributes = new Attribute[] { attribute };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var context = new ValidationMetadataProviderContext(key, attributes);
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(attributes, new object[0]));
// Act
provider.GetValidationMetadata(context);
@ -651,7 +651,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var attribute = new TestValidationAttribute();
var attributes = new Attribute[] { attribute };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var context = new ValidationMetadataProviderContext(key, attributes);
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(attributes, new object[0]));
context.ValidationMetadata.ValidatorMetadata.Add(attribute);
// Act

View File

@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
typeof(string),
nameof(ClassWithDataMemberIsRequiredTrue.StringProperty),
typeof(ClassWithDataMemberIsRequiredTrue));
var context = new BindingMetadataProviderContext(key, attributes);
var context = new BindingMetadataProviderContext(key, new ModelAttributes(attributes, new object[0]));
// Act
provider.GetBindingMetadata(context);
@ -49,7 +49,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
typeof(string),
nameof(ClassWithDataMemberIsRequiredFalse.StringProperty),
typeof(ClassWithDataMemberIsRequiredFalse));
var context = new BindingMetadataProviderContext(key, attributes);
var context = new BindingMetadataProviderContext(key, new ModelAttributes(attributes, new object[0]));
context.BindingMetadata.IsBindingRequired = initialValue;
@ -74,7 +74,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
};
var key = ModelMetadataIdentity.ForType(typeof(ClassWithDataMemberIsRequiredTrue));
var context = new BindingMetadataProviderContext(key, attributes);
var context = new BindingMetadataProviderContext(key, new ModelAttributes(attributes, new object[0]));
context.BindingMetadata.IsBindingRequired = initialValue;
@ -97,7 +97,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
typeof(string),
nameof(ClassWithoutAttributes.StringProperty),
typeof(ClassWithoutAttributes));
var context = new BindingMetadataProviderContext(key, attributes: new object[0]);
var context = new BindingMetadataProviderContext(key, new ModelAttributes(new object[0], new object[0]));
context.BindingMetadata.IsBindingRequired = initialValue;
@ -125,7 +125,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
typeof(string),
nameof(ClassWithDataMemberIsRequiredTrueWithoutDataContract.StringProperty),
typeof(ClassWithDataMemberIsRequiredTrueWithoutDataContract));
var context = new BindingMetadataProviderContext(key, attributes);
var context = new BindingMetadataProviderContext(key, new ModelAttributes(attributes, new object[0]));
context.BindingMetadata.IsBindingRequired = initialValue;

View File

@ -18,8 +18,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
};
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForType(typeof(string)),
attributes);
ModelMetadataIdentity.ForType(typeof(string)),
new ModelAttributes(attributes));
var provider = new DefaultBindingMetadataProvider();
@ -43,7 +43,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForType(typeof(string)),
attributes);
new ModelAttributes(attributes));
var provider = new DefaultBindingMetadataProvider();
@ -66,7 +66,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForType(typeof(string)),
attributes);
new ModelAttributes(attributes));
var provider = new DefaultBindingMetadataProvider();
@ -90,7 +90,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForType(typeof(string)),
attributes);
new ModelAttributes(attributes));
var provider = new DefaultBindingMetadataProvider();
@ -113,7 +113,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForType(typeof(string)),
attributes);
new ModelAttributes(attributes));
var provider = new DefaultBindingMetadataProvider();
@ -137,7 +137,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var context = new BindingMetadataProviderContext(
ModelMetadataIdentity.ForType(typeof(string)),
attributes);
new ModelAttributes(attributes));
var provider = new DefaultBindingMetadataProvider();

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
// Assert
var defaultMetadata = Assert.IsType<DefaultModelMetadata>(metadata);
var attribute = Assert.IsType<ModelAttribute>(Assert.Single(defaultMetadata.Attributes));
var attribute = Assert.IsType<ModelAttribute>(Assert.Single(defaultMetadata.Attributes.Attributes));
Assert.Equal("OnType", attribute.Value);
}
@ -121,7 +121,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
// Assert
var defaultMetadata = Assert.IsType<DefaultModelMetadata>(metadata);
var attributes = defaultMetadata.Attributes.ToArray();
var attributes = defaultMetadata.Attributes.Attributes.ToArray();
Assert.Equal("OnProperty", Assert.IsType<ModelAttribute>(attributes[0]).Value);
Assert.Equal("OnPropertyType", Assert.IsType<ModelAttribute>(attributes[1]).Value);
}

View File

@ -22,7 +22,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
Enumerable.Empty<IMetadataDetailsProvider>());
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new object[0]);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
// Act
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
@ -76,7 +76,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
Enumerable.Empty<IMetadataDetailsProvider>());
var key = ModelMetadataIdentity.ForType(typeof(Exception));
var cache = new DefaultMetadataDetails(key, new object[0]);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
// Act
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
@ -95,7 +95,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForProperty(typeof(string), "Message", typeof(Exception));
var cache = new DefaultMetadataDetails(key, new object[0]);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0], new object[0]));
// Act
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
@ -117,7 +117,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(modelType);
var cache = new DefaultMetadataDetails(key, new object[0]);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
@ -138,7 +138,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(modelType);
var cache = new DefaultMetadataDetails(key, new object[0]);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
@ -164,13 +164,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
detailsProvider,
new DefaultMetadataDetails(
ModelMetadataIdentity.ForProperty(typeof(int), "Prop1", typeof(string)),
attributes: null)),
attributes: new ModelAttributes(new object[0], new object[0]))),
new DefaultModelMetadata(
provider.Object,
detailsProvider,
new DefaultMetadataDetails(
ModelMetadataIdentity.ForProperty(typeof(int), "Prop2", typeof(string)),
attributes: null)),
attributes: new ModelAttributes(new object[0], new object[0]))),
};
provider
@ -178,7 +178,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
.Returns(expectedProperties);
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new object[0]);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
var metadata = new DefaultModelMetadata(provider.Object, detailsProvider, cache);
@ -240,7 +240,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
detailsProvider,
new DefaultMetadataDetails(
ModelMetadataIdentity.ForProperty(typeof(int), originalName, typeof(string)),
attributes: null)));
attributes: new ModelAttributes(new object[0], new object[0]))));
}
provider
@ -248,7 +248,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
.Returns(expectedProperties);
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new object[0]);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
var metadata = new DefaultModelMetadata(provider.Object, detailsProvider, cache);
@ -340,7 +340,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
{
var propertyCache = new DefaultMetadataDetails(
ModelMetadataIdentity.ForProperty(typeof(int), kvp.Key, typeof(string)),
attributes: null);
attributes: new ModelAttributes(new object[0], new object[0]));
propertyCache.DisplayMetadata = new DisplayMetadata();
propertyCache.DisplayMetadata.Order = kvp.Value;
@ -356,7 +356,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
.Returns(expectedProperties);
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new object[0]);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
var metadata = new DefaultModelMetadata(provider.Object, detailsProvider, cache);
@ -377,7 +377,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new object[0]);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
@ -398,7 +398,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var detailsProvider = new EmptyCompositeMetadataDetailsProvider();
var key = ModelMetadataIdentity.ForType(typeof(string));
var cache = new DefaultMetadataDetails(key, new object[0]);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
@ -419,7 +419,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(typeof(int[]));
var cache = new DefaultMetadataDetails(key, new object[0]);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
@ -438,7 +438,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(typeof(TypeWithProperties));
var cache = new DefaultMetadataDetails(key, new object[0]);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
@ -457,7 +457,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(typeof(TypeWithProperties));
var cache = new DefaultMetadataDetails(key, new object[0]);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);
@ -476,7 +476,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var provider = new DefaultModelMetadataProvider(detailsProvider);
var key = ModelMetadataIdentity.ForType(typeof(TypeWithProperties));
var cache = new DefaultMetadataDetails(key, new object[0]);
var cache = new DefaultMetadataDetails(key, new ModelAttributes(new object[0]));
var metadata = new DefaultModelMetadata(provider, detailsProvider, cache);

View File

@ -19,7 +19,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var attribute = new TestClientModelValidationAttribute();
var attributes = new Attribute[] { attribute };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var context = new ValidationMetadataProviderContext(key, attributes);
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(attributes, new object[0]));
// Act
provider.GetValidationMetadata(context);
@ -38,7 +38,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var attribute = new TestModelValidationAttribute();
var attributes = new Attribute[] { attribute };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var context = new ValidationMetadataProviderContext(key, attributes);
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(attributes, new object[0]));
// Act
provider.GetValidationMetadata(context);
@ -57,7 +57,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
var attribute = new TestValidationAttribute();
var attributes = new Attribute[] { attribute };
var key = ModelMetadataIdentity.ForProperty(typeof(int), "Length", typeof(string));
var context = new ValidationMetadataProviderContext(key, attributes);
var context = new ValidationMetadataProviderContext(key, new ModelAttributes(attributes, new object[0]));
context.ValidationMetadata.ValidatorMetadata.Add(attribute);
// Act

View File

@ -9,7 +9,7 @@ using Xunit;
namespace Microsoft.AspNet.Mvc.ModelBinding
{
public class ModelMetadataAttributesTest
public class ModelAttributesTest
{
[Fact]
public void GetAttributesForBaseProperty_IncludesMetadataAttributes()
@ -22,8 +22,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
// Assert
Assert.Single(attributes.OfType<RequiredAttribute>());
Assert.Single(attributes.OfType<StringLengthAttribute>());
Assert.Single(attributes.Attributes.OfType<RequiredAttribute>());
Assert.Single(attributes.Attributes.OfType<StringLengthAttribute>());
Assert.Single(attributes.PropertyAttributes.OfType<RequiredAttribute>());
Assert.Single(attributes.PropertyAttributes.OfType<StringLengthAttribute>());
}
[Fact]
@ -37,11 +40,23 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
// Assert
var rangeAttribute = attributes.OfType<RangeAttribute>().FirstOrDefault();
Assert.NotNull(rangeAttribute);
Assert.Equal(0, (int)rangeAttribute.Minimum);
Assert.Equal(10, (int)rangeAttribute.Maximum);
Assert.Single(attributes.OfType<FromHeaderAttribute>());
var rangeAttributes = attributes.Attributes.OfType<RangeAttribute>().ToArray();
Assert.NotNull(rangeAttributes[0]);
Assert.Equal(0, (int)rangeAttributes[0].Minimum);
Assert.Equal(10, (int)rangeAttributes[0].Maximum);
Assert.NotNull(rangeAttributes[1]);
Assert.Equal(10, (int)rangeAttributes[1].Minimum);
Assert.Equal(100, (int)rangeAttributes[1].Maximum);
Assert.Single(attributes.Attributes.OfType<FromHeaderAttribute>());
rangeAttributes = attributes.PropertyAttributes.OfType<RangeAttribute>().ToArray();
Assert.NotNull(rangeAttributes[0]);
Assert.Equal(0, (int)rangeAttributes[0].Minimum);
Assert.Equal(10, (int)rangeAttributes[0].Maximum);
Assert.NotNull(rangeAttributes[1]);
Assert.Equal(10, (int)rangeAttributes[1].Minimum);
Assert.Equal(100, (int)rangeAttributes[1].Maximum);
Assert.Single(attributes.PropertyAttributes.OfType<FromHeaderAttribute>());
}
[Fact]
@ -55,8 +70,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
// Assert
Assert.Single(attributes.OfType<RequiredAttribute>());
Assert.Single(attributes.OfType<StringLengthAttribute>());
Assert.Single(attributes.Attributes.OfType<RequiredAttribute>());
Assert.Single(attributes.Attributes.OfType<StringLengthAttribute>());
Assert.Single(attributes.PropertyAttributes.OfType<RequiredAttribute>());
Assert.Single(attributes.PropertyAttributes.OfType<StringLengthAttribute>());
}
[Fact]
@ -70,9 +88,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
// Assert
Assert.Single(attributes.OfType<RequiredAttribute>());
Assert.Single(attributes.OfType<StringLengthAttribute>());
Assert.DoesNotContain(typeof(RangeAttribute), attributes);
Assert.Single(attributes.Attributes.OfType<RequiredAttribute>());
Assert.Single(attributes.Attributes.OfType<StringLengthAttribute>());
Assert.DoesNotContain(typeof(RangeAttribute), attributes.Attributes);
Assert.Single(attributes.PropertyAttributes.OfType<RequiredAttribute>());
Assert.Single(attributes.PropertyAttributes.OfType<StringLengthAttribute>());
Assert.DoesNotContain(typeof(RangeAttribute), attributes.PropertyAttributes);
}
[Fact]
@ -86,8 +108,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
// Assert
Assert.Single(attributes.OfType<RequiredAttribute>());
Assert.Single(attributes.OfType<RangeAttribute>());
Assert.Single(attributes.Attributes.OfType<RequiredAttribute>());
Assert.Single(attributes.Attributes.OfType<RangeAttribute>());
Assert.Single(attributes.PropertyAttributes.OfType<RequiredAttribute>());
Assert.Single(attributes.PropertyAttributes.OfType<RangeAttribute>());
}
[Fact]
@ -101,8 +126,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
// Assert
Assert.Single(attributes.OfType<RequiredAttribute>());
Assert.Single(attributes.OfType<FromServicesAttribute>());
Assert.Single(attributes.Attributes.OfType<RequiredAttribute>());
Assert.Single(attributes.Attributes.OfType<FromServicesAttribute>());
Assert.Single(attributes.PropertyAttributes.OfType<RequiredAttribute>());
Assert.Single(attributes.PropertyAttributes.OfType<FromServicesAttribute>());
}
[Fact]
@ -112,10 +140,45 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
var attributes = ModelAttributes.GetAttributesForType(typeof(BaseViewModel));
// Assert
Assert.Single(attributes.OfType<ClassValidator>());
Assert.Single(attributes.Attributes.OfType<ClassValidator>());
Assert.Single(attributes.TypeAttributes.OfType<ClassValidator>());
}
// Helper classes
[Fact]
public void GetAttributesForType_PropertyAttributes_IsNull()
{
// Arrange & Act
var attributes = ModelAttributes.GetAttributesForType(typeof(BaseViewModel));
// Assert
Assert.Null(attributes.PropertyAttributes);
}
[Fact]
public void GetAttributesForProperty_MergedAttributes()
{
// Arrange
var property = typeof(MergedAttributes).GetRuntimeProperty(nameof(MergedAttributes.Property));
// Act
var attributes = ModelAttributes.GetAttributesForProperty(typeof(MergedAttributes), property);
// Assert
Assert.Equal(4, attributes.Attributes.Count);
Assert.IsType<RequiredAttribute>(attributes.Attributes[0]);
Assert.IsType<RangeAttribute>(attributes.Attributes[1]);
Assert.IsType<ClassValidator>(attributes.Attributes[2]);
Assert.IsType<BindAttribute>(attributes.Attributes[3]);
Assert.Equal(2, attributes.PropertyAttributes.Count);
Assert.IsType<RequiredAttribute>(attributes.PropertyAttributes[0]);
Assert.IsType<RangeAttribute>(attributes.PropertyAttributes[1]);
Assert.Equal(2, attributes.TypeAttributes.Count);
Assert.IsType<ClassValidator>(attributes.TypeAttributes[0]);
Assert.IsType<BindAttribute>(attributes.TypeAttributes[1]);
}
[ClassValidator]
private class BaseModel
@ -182,5 +245,28 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
return true;
}
}
[ModelMetadataType(typeof(MergedAttributesMetadata))]
private class MergedAttributes
{
[Required]
public PropertyType Property { get; set; }
}
private class MergedAttributesMetadata
{
[Range(0, 10)]
public MetadataPropertyType Property { get; set; }
}
[ClassValidator]
private class PropertyType
{
}
[Bind]
private class MetadataPropertyType
{
}
}
}

View File

@ -886,7 +886,9 @@ namespace Microsoft.AspNet.Mvc.ModelBinding.Metadata
protected override DefaultMetadataDetails CreateTypeDetails([NotNull]ModelMetadataIdentity key)
{
var entry = base.CreateTypeDetails(key);
return new DefaultMetadataDetails(key, _attributes.Concat(entry.Attributes).ToArray());
return new DefaultMetadataDetails(
key,
new ModelAttributes(_attributes.Concat(entry.ModelAttributes.TypeAttributes).ToArray()));
}
}
}

View File

@ -20,7 +20,7 @@ namespace ModelBindingWebSite
protected override Task<ModelBindingResult> BindModelCoreAsync(ModelBindingContext bindingContext)
{
var attributes = ((DefaultModelMetadata)bindingContext.ModelMetadata).Attributes;
var metadata = attributes.OfType<FromTestAttribute>().First();
var metadata = attributes.Attributes.OfType<FromTestAttribute>().First();
var model = metadata.Value;
if (!IsSimpleType(bindingContext.ModelType))
{