Added ModelMetadataType Attribute and logic to get attributes from ModelMetadata class
This commit is contained in:
parent
6641997836
commit
6a824a4394
|
|
@ -157,7 +157,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
private TModelMetadata CreateTypeInformation(Type type, IEnumerable<Attribute> associatedAttributes)
|
||||
{
|
||||
var attributes = type.GetTypeInfo().GetCustomAttributes();
|
||||
var attributes = ModelAttributes.GetAttributesForType(type);
|
||||
if (associatedAttributes != null)
|
||||
{
|
||||
attributes = attributes.Concat(associatedAttributes);
|
||||
|
|
@ -169,10 +169,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
private PropertyInformation CreatePropertyInformation(Type containerType, PropertyHelper helper)
|
||||
{
|
||||
var property = helper.Property;
|
||||
var attributes = ModelAttributes.GetAttributesForProperty(containerType, property);
|
||||
|
||||
return new PropertyInformation
|
||||
{
|
||||
PropertyHelper = helper,
|
||||
Prototype = CreateMetadataPrototype(ModelAttributes.GetAttributesForProperty(property),
|
||||
Prototype = CreateMetadataPrototype(attributes,
|
||||
containerType,
|
||||
property.PropertyType,
|
||||
property.Name),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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 System.Linq;
|
||||
using System.Reflection;
|
||||
|
|
@ -32,17 +33,57 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
/// <summary>
|
||||
/// Gets the attributes for the given <paramref name="property"/>.
|
||||
/// </summary>
|
||||
/// <param name="property">A <see cref="ParameterInfo"/> for which attributes need to be resolved.
|
||||
/// <param name="type">The <see cref="Type"/> in which caller found <paramref name="property"/>.
|
||||
/// </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(PropertyInfo property)
|
||||
public static IEnumerable<object> GetAttributesForProperty([NotNull] Type type, [NotNull] PropertyInfo property)
|
||||
{
|
||||
// Return the property attributes first.
|
||||
var propertyAttributes = property.GetCustomAttributes();
|
||||
var typeAttributes = property.PropertyType.GetTypeInfo().GetCustomAttributes();
|
||||
|
||||
return propertyAttributes.Concat(typeAttributes);
|
||||
propertyAttributes = propertyAttributes.Concat(typeAttributes);
|
||||
|
||||
var modelMedatadataType = type.GetTypeInfo().GetCustomAttribute<ModelMetadataTypeAttribute>();
|
||||
if (modelMedatadataType != null)
|
||||
{
|
||||
var modelMedatadataProperty = modelMedatadataType.MetadataType.GetRuntimeProperty(property.Name);
|
||||
if (modelMedatadataProperty != null)
|
||||
{
|
||||
var modelMedatadataAttributes = modelMedatadataProperty.GetCustomAttributes();
|
||||
propertyAttributes = propertyAttributes.Concat(modelMedatadataAttributes);
|
||||
|
||||
var modelMetadataTypeAttributes =
|
||||
modelMedatadataProperty.PropertyType.GetTypeInfo().GetCustomAttributes();
|
||||
propertyAttributes = propertyAttributes.Concat(modelMetadataTypeAttributes);
|
||||
}
|
||||
}
|
||||
|
||||
return propertyAttributes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attributes for the given <paramref name="type"/>.
|
||||
/// </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)
|
||||
{
|
||||
var attributes = type.GetTypeInfo().GetCustomAttributes();
|
||||
|
||||
var modelMedatadataType = type.GetTypeInfo().GetCustomAttribute<ModelMetadataTypeAttribute>();
|
||||
if (modelMedatadataType != null)
|
||||
{
|
||||
var modelMedatadataAttributes = modelMedatadataType.MetadataType.GetTypeInfo().GetCustomAttributes();
|
||||
attributes = attributes.Concat(modelMedatadataAttributes);
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,9 +14,4 @@
|
|||
<SchemaVersion>2.0</SchemaVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" />
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
<UserProperties project_1json__JSONSchema="http://www.asp.net/media/4878834/project.json" />
|
||||
</VisualStudio>
|
||||
</ProjectExtensions>
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc
|
||||
{
|
||||
/// <summary>
|
||||
/// This attribute specifies the metadata class to associate with a data model class.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
|
||||
public class ModelMetadataTypeAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ModelMetadataTypeAttribute" /> class.
|
||||
/// </summary>
|
||||
/// <param name="type">The type of metadata class that is associated with a data model class.</param>
|
||||
public ModelMetadataTypeAttribute([NotNull] Type type)
|
||||
{
|
||||
MetadataType = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of metadata class that is associated with a data model class.
|
||||
/// </summary>
|
||||
public Type MetadataType { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
|
|
@ -20,7 +21,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
|
||||
protected abstract IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata,
|
||||
IEnumerable<Attribute> attributes);
|
||||
IEnumerable<object> attributes);
|
||||
|
||||
private IEnumerable<IModelValidator> GetValidatorsForProperty(ModelMetadata metadata)
|
||||
{
|
||||
|
|
@ -38,15 +39,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
"metadata");
|
||||
}
|
||||
|
||||
var attributes = property.GetCustomAttributes();
|
||||
var attributes = ModelAttributes.GetAttributesForProperty(metadata.ContainerType, property);
|
||||
return GetValidators(metadata, attributes);
|
||||
}
|
||||
|
||||
private IEnumerable<IModelValidator> GetValidatorsForType(ModelMetadata metadata)
|
||||
{
|
||||
var attributes = metadata.ModelType
|
||||
.GetTypeInfo()
|
||||
.GetCustomAttributes();
|
||||
var attributes = ModelAttributes.GetAttributesForType(metadata.ModelType);
|
||||
|
||||
return GetValidators(metadata, attributes);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
}
|
||||
|
||||
protected override IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata,
|
||||
IEnumerable<Attribute> attributes)
|
||||
IEnumerable<object> attributes)
|
||||
{
|
||||
var results = new List<IModelValidator>();
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
public class DataMemberModelValidatorProvider : AssociatedValidatorProvider
|
||||
{
|
||||
protected override IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata,
|
||||
IEnumerable<Attribute> attributes)
|
||||
IEnumerable<object> attributes)
|
||||
{
|
||||
// Types cannot be required; only properties can
|
||||
if (metadata.ContainerType == null || string.IsNullOrEmpty(metadata.PropertyName))
|
||||
|
|
@ -32,7 +32,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
return Enumerable.Empty<IModelValidator>();
|
||||
}
|
||||
|
||||
internal static bool IsRequiredDataMember(Type containerType, IEnumerable<Attribute> attributes)
|
||||
internal static bool IsRequiredDataMember(Type containerType, IEnumerable<object> attributes)
|
||||
{
|
||||
var dataMemberAttribute = attributes.OfType<DataMemberAttribute>()
|
||||
.FirstOrDefault();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,186 @@
|
|||
// Copyright (c) Microsoft Open Technologies, Inc. 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.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||
{
|
||||
public class ModelMetadataAttributesTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetAttributesForBaseProperty_IncludesMetadataAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var modelType = typeof(BaseViewModel);
|
||||
var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == "BaseProperty");
|
||||
|
||||
// Act
|
||||
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
|
||||
|
||||
// Assert
|
||||
Assert.Single(attributes.OfType<RequiredAttribute>());
|
||||
Assert.Single(attributes.OfType<StringLengthAttribute>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributesForTestProperty_ModelOverridesMetadataAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var modelType = typeof(BaseViewModel);
|
||||
var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == "TestProperty");
|
||||
|
||||
// Act
|
||||
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>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributesForBasePropertyFromDerivedModel_IncludesMetadataAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var modelType = typeof(DerivedViewModel);
|
||||
var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == "BaseProperty");
|
||||
|
||||
// Act
|
||||
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
|
||||
|
||||
// Assert
|
||||
Assert.Single(attributes.OfType<RequiredAttribute>());
|
||||
Assert.Single(attributes.OfType<StringLengthAttribute>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributesForTestPropertyFromDerived_IncludesMetadataAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var modelType = typeof(DerivedViewModel);
|
||||
var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == "TestProperty");
|
||||
|
||||
// Act
|
||||
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
|
||||
|
||||
// Assert
|
||||
Assert.Single(attributes.OfType<RequiredAttribute>());
|
||||
Assert.Single(attributes.OfType<StringLengthAttribute>());
|
||||
Assert.DoesNotContain(typeof(RangeAttribute), attributes);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributesForVirtualPropertyFromDerived_IncludesMetadataAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var modelType = typeof(DerivedViewModel);
|
||||
var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == "VirtualProperty");
|
||||
|
||||
// Act
|
||||
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
|
||||
|
||||
// Assert
|
||||
Assert.Single(attributes.OfType<RequiredAttribute>());
|
||||
Assert.Single(attributes.OfType<RangeAttribute>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetFromServiceAttributeFromBase_IncludesMetadataAttributes()
|
||||
{
|
||||
// Arrange
|
||||
var modelType = typeof(DerivedViewModel);
|
||||
var property = modelType.GetRuntimeProperties().FirstOrDefault(p => p.Name == "Calculator");
|
||||
|
||||
// Act
|
||||
var attributes = ModelAttributes.GetAttributesForProperty(modelType, property);
|
||||
|
||||
// Assert
|
||||
Assert.Single(attributes.OfType<RequiredAttribute>());
|
||||
Assert.Single(attributes.OfType<FromServicesAttribute>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetAttributesForType_IncludesMetadataAttributes()
|
||||
{
|
||||
// Arrange & Act
|
||||
var attributes = ModelAttributes.GetAttributesForType(typeof(BaseViewModel));
|
||||
|
||||
// Assert
|
||||
Assert.Single(attributes.OfType<ClassValidator>());
|
||||
}
|
||||
|
||||
// Helper classes
|
||||
|
||||
[ClassValidator]
|
||||
private class BaseModel
|
||||
{
|
||||
[StringLength(10)]
|
||||
public string BaseProperty { get; set; }
|
||||
|
||||
[Range(10,100)]
|
||||
[FromHeader]
|
||||
public string TestProperty { get; set; }
|
||||
|
||||
[Required]
|
||||
public virtual int VirtualProperty { get; set; }
|
||||
|
||||
[FromServices]
|
||||
public ICalculator Calculator { get; set; }
|
||||
}
|
||||
|
||||
private class DerivedModel : BaseModel
|
||||
{
|
||||
[Required]
|
||||
public string DerivedProperty { get; set; }
|
||||
|
||||
[Required]
|
||||
public new string TestProperty { get; set; }
|
||||
|
||||
[Range(10,100)]
|
||||
public override int VirtualProperty { get; set; }
|
||||
|
||||
}
|
||||
|
||||
[ModelMetadataType(typeof(BaseModel))]
|
||||
private class BaseViewModel
|
||||
{
|
||||
[Range(0,10)]
|
||||
public string TestProperty { get; set; }
|
||||
|
||||
[Required]
|
||||
public string BaseProperty { get; set; }
|
||||
|
||||
[Required]
|
||||
public ICalculator Calculator { get; set; }
|
||||
}
|
||||
|
||||
[ModelMetadataType(typeof(DerivedModel))]
|
||||
private class DerivedViewModel : BaseViewModel
|
||||
{
|
||||
[StringLength(2)]
|
||||
public new string TestProperty { get; set; }
|
||||
|
||||
public int VirtualProperty { get; set; }
|
||||
|
||||
}
|
||||
|
||||
public interface ICalculator
|
||||
{
|
||||
int Operation(char @operator, int left, int right);
|
||||
}
|
||||
|
||||
private class ClassValidator : ValidationAttribute
|
||||
{
|
||||
public override Boolean IsValid(Object value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -91,12 +91,12 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
|||
|
||||
public abstract class TestableAssociatedValidatorProvider : AssociatedValidatorProvider
|
||||
{
|
||||
protected override IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata, IEnumerable<Attribute> attributes)
|
||||
protected override IEnumerable<IModelValidator> GetValidators(ModelMetadata metadata, IEnumerable<object> attributes)
|
||||
{
|
||||
return AbstractGetValidators(metadata, attributes);
|
||||
}
|
||||
|
||||
public abstract IEnumerable<IModelValidator> AbstractGetValidators(ModelMetadata metadata, IEnumerable<Attribute> attributes);
|
||||
public abstract IEnumerable<IModelValidator> AbstractGetValidators(ModelMetadata metadata, IEnumerable<object> attributes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue