Support ModelMetaData.IsRequired
* Update DataAnnotationsModelValidatorProvider to use ModelMetadata.IsRequired * Adding tests and updating existing ones that didn't work with IsRequired Fixes #533
This commit is contained in:
parent
95aa6ad607
commit
7396f58d99
|
|
@ -15,6 +15,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
Display = attributes.OfType<DisplayAttribute>().FirstOrDefault();
|
Display = attributes.OfType<DisplayAttribute>().FirstOrDefault();
|
||||||
DisplayFormat = attributes.OfType<DisplayFormatAttribute>().FirstOrDefault();
|
DisplayFormat = attributes.OfType<DisplayFormatAttribute>().FirstOrDefault();
|
||||||
Editable = attributes.OfType<EditableAttribute>().FirstOrDefault();
|
Editable = attributes.OfType<EditableAttribute>().FirstOrDefault();
|
||||||
|
Required = attributes.OfType<RequiredAttribute>().FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisplayAttribute Display { get; protected set; }
|
public DisplayAttribute Display { get; protected set; }
|
||||||
|
|
@ -22,5 +23,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
public DisplayFormatAttribute DisplayFormat { get; protected set; }
|
public DisplayFormatAttribute DisplayFormat { get; protected set; }
|
||||||
|
|
||||||
public EditableAttribute Editable { get; protected set; }
|
public EditableAttribute Editable { get; protected set; }
|
||||||
|
|
||||||
|
public RequiredAttribute Required { get; protected set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
return base.ComputeIsReadOnly();
|
return base.ComputeIsReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool ComputeIsRequired()
|
||||||
|
{
|
||||||
|
return (PrototypeCache.Required != null) || base.ComputeIsRequired();
|
||||||
|
}
|
||||||
|
|
||||||
public override string GetDisplayName()
|
public override string GetDisplayName()
|
||||||
{
|
{
|
||||||
// DisplayAttribute doesn't require you to set a name, so this could be null.
|
// DisplayAttribute doesn't require you to set a name, so this could be null.
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,14 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
private string _description;
|
private string _description;
|
||||||
private bool _isReadOnly;
|
private bool _isReadOnly;
|
||||||
private bool _isComplexType;
|
private bool _isComplexType;
|
||||||
|
private bool _isRequired;
|
||||||
|
|
||||||
private bool _convertEmptyStringToNullComputed;
|
private bool _convertEmptyStringToNullComputed;
|
||||||
private bool _nullDisplayTextComputed;
|
private bool _nullDisplayTextComputed;
|
||||||
private bool _descriptionComputed;
|
private bool _descriptionComputed;
|
||||||
private bool _isReadOnlyComputed;
|
private bool _isReadOnlyComputed;
|
||||||
private bool _isComplexTypeComputed;
|
private bool _isComplexTypeComputed;
|
||||||
|
private bool _isRequiredComputed;
|
||||||
|
|
||||||
// Constructor for creating real instances of the metadata class based on a prototype
|
// Constructor for creating real instances of the metadata class based on a prototype
|
||||||
protected CachedModelMetadata(CachedModelMetadata<TPrototypeCache> prototype, Func<object> modelAccessor)
|
protected CachedModelMetadata(CachedModelMetadata<TPrototypeCache> prototype, Func<object> modelAccessor)
|
||||||
|
|
@ -125,6 +127,24 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed override bool IsRequired
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!_isRequiredComputed)
|
||||||
|
{
|
||||||
|
_isRequired = ComputeIsRequired();
|
||||||
|
_isRequiredComputed = true;
|
||||||
|
}
|
||||||
|
return _isRequired;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_isRequired = value;
|
||||||
|
_isRequiredComputed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public sealed override bool IsComplexType
|
public sealed override bool IsComplexType
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
@ -160,6 +180,11 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
return base.IsReadOnly;
|
return base.IsReadOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual bool ComputeIsRequired()
|
||||||
|
{
|
||||||
|
return base.IsRequired;
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual bool ComputeIsComplexType()
|
protected virtual bool ComputeIsComplexType()
|
||||||
{
|
{
|
||||||
return base.IsComplexType;
|
return base.IsComplexType;
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
_modelAccessor = modelAccessor;
|
_modelAccessor = modelAccessor;
|
||||||
_modelType = modelType;
|
_modelType = modelType;
|
||||||
_propertyName = propertyName;
|
_propertyName = propertyName;
|
||||||
|
IsRequired = !modelType.AllowsNullValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Type ContainerType
|
public Type ContainerType
|
||||||
|
|
@ -71,6 +72,8 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
|
|
||||||
public virtual bool IsReadOnly { get; set; }
|
public virtual bool IsReadOnly { get; set; }
|
||||||
|
|
||||||
|
public virtual bool IsRequired { get; set; }
|
||||||
|
|
||||||
public virtual int Order
|
public virtual int Order
|
||||||
{
|
{
|
||||||
get { return _order; }
|
get { return _order; }
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,49 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
Assert.False(new ModelMetadata(provider.Object, null, null, typeof(int), null).IsNullableValueType);
|
Assert.False(new ModelMetadata(provider.Object, null, null, typeof(int), null).IsNullableValueType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsRequired
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(typeof(string))]
|
||||||
|
[InlineData(typeof(IDisposable))]
|
||||||
|
[InlineData(typeof(Nullable<int>))]
|
||||||
|
public void IsRequired_ReturnsFalse_ForNullableTypes(Type modelType)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var provider = new Mock<IModelMetadataProvider>();
|
||||||
|
var metadata = new ModelMetadata(provider.Object,
|
||||||
|
containerType: null,
|
||||||
|
modelAccessor: null,
|
||||||
|
modelType: modelType,
|
||||||
|
propertyName: null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var isRequired = metadata.IsRequired;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.False(isRequired);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(typeof(int))]
|
||||||
|
[InlineData(typeof(DayOfWeek))]
|
||||||
|
public void IsRequired_ReturnsTrue_ForNonNullableTypes(Type modelType)
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var provider = new Mock<IModelMetadataProvider>();
|
||||||
|
var metadata = new ModelMetadata(provider.Object,
|
||||||
|
containerType: null,
|
||||||
|
modelAccessor: null,
|
||||||
|
modelType: modelType,
|
||||||
|
propertyName: null);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var isRequired = metadata.IsRequired;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.True(isRequired);
|
||||||
|
}
|
||||||
|
|
||||||
// Properties
|
// Properties
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,41 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
{
|
{
|
||||||
private readonly DataAnnotationsModelMetadataProvider _metadataProvider = new DataAnnotationsModelMetadataProvider();
|
private readonly DataAnnotationsModelMetadataProvider _metadataProvider = new DataAnnotationsModelMetadataProvider();
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetValidators_ReturnsValidatorForIValidatableObject()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var provider = new DataAnnotationsModelValidatorProvider();
|
||||||
|
var mockValidatable = Mock.Of<IValidatableObject>();
|
||||||
|
var metadata = _metadataProvider.GetMetadataForType(() => null, mockValidatable.GetType());
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var validators = provider.GetValidators(metadata);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var validator = Assert.Single(validators);
|
||||||
|
Assert.IsType<ValidatableObjectAdapter>(validator);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetValidators_DoesNotAddRequiredAttribute_ForNonNullableValueTypes_IfAttributeIsSpecifiedExplicitly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var provider = new DataAnnotationsModelValidatorProvider();
|
||||||
|
var metadata = _metadataProvider.GetMetadataForProperty(() => null,
|
||||||
|
typeof(DummyRequiredAttributeHelperClass),
|
||||||
|
"WithAttribute");
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var validators = provider.GetValidators(metadata);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var validator = Assert.Single(validators);
|
||||||
|
var adapter = Assert.IsType<RequiredAttributeAdapter>(validator);
|
||||||
|
Assert.Equal("Custom Required Message", adapter.Attribute.ErrorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
public static IEnumerable<object[]> DataAnnotationAdapters
|
public static IEnumerable<object[]> DataAnnotationAdapters
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|
@ -198,5 +233,13 @@ namespace Microsoft.AspNet.Mvc.ModelBinding
|
||||||
set { base.MyProperty = value; }
|
set { base.MyProperty = value; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class DummyRequiredAttributeHelperClass
|
||||||
|
{
|
||||||
|
[Required(ErrorMessage = "Custom Required Message")]
|
||||||
|
public int WithAttribute { get; set; }
|
||||||
|
|
||||||
|
public int WithoutAttribute { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue