I have a simple model:
public class Sample
{
public bool A { get; set; }
[Required]
public bool B { get; set; }
}
A is obviously not required. Therefore, for validation have have set DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false
in Global.asax.
I also have a simple html helper that prints true or false if the model is required:
public static class HtmlHelperExtensions
{
public static MvcHtmlString IsRequired<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, html.ViewData);
return new MvcHtmlString(metadata.IsRequired.ToString());
}
}
I also wrote a view to showcase my issue:
@model MvcApplication10.Models.Sample
A: @Html.IsRequired(m => m.A), B: @Html.IsRequired(m => m.B)
I would have expected this to print A: false, B: true
, however, it actually prints A: true, B: true
.
Is there any way to make this print my expected result? IsRequired
seems to always return true even though I have not explicitly set the RequiredAttribute
. The docs state that it is true for non-nullable value types by default. How come there is no easy way to set this to false like we can with validation?
EDIT: I could write a custom provider like this, but I was wondering if there was an "easy" way around this:
public class ExtendedDataAnnotationsModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
private static bool addImplicitRequiredAttributeForValueTypes = false;
public static bool AddImplicitRequiredAttributeForValueTypes
{
get
{
return addImplicitRequiredAttributeForValueTypes;
}
set
{
addImplicitRequiredAttributeForValueTypes = value;
}
}
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var result = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
if (!AddImplicitRequiredAttributeForValueTypes && modelType.IsValueType && !attributes.OfType<RequiredAttribute>().Any())
{
result.IsRequired = false;
}
return result;
}
}
As you noted, ValueTypes will default to true. To work around that, you can check for the RequiredAttribute
if the type is a ValueType
.
ModelMetadata metaData = ModelMetadata.FromLambdaExpression<TModel, TValue>(expression, html.ViewData);
if ((metaData.ModelType.IsValueType && metaData.ModelType.GetCustomAttributes(typeof(RequiredAttribute), false).Any()) ||
(!metaData.ModelType.IsValueType && metaData.IsRequired))
{ ... }
I guess you're facing a MVC bug. Required will always trigger on that situation, no matter what and even if you use
DataAnnotationsModelValidatorProvider
.AddImplicitRequiredAttributeForValueTypes = false;
This was already discussed here and reported here. This example goes a step further and shows that when an implicit required triggers it doesn't prevent IValidatableObject
from execute. If you run the demo from the second link you can reproduce your case, where required is always true.
Anyway, this is easy to solve because if you're saying that A is obviously not required
is the same as saying that it is nullable, so just do it that way:
public bool? A { get; set; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With