如何禁用ModelMetadata.IsRequired始终为非可空值类型的true

时间:2011-12-02 23:31:50

标签: c# .net asp.net-mvc asp.net-mvc-3

我有一个简单的模型:

public class Sample
{
    public bool A { get; set; }

    [Required]
    public bool B { get; set; }
}
显然不需要A.因此,验证已在Global.asax中设置DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false

我还有一个简单的html帮助器,如果需要模型,则打印true或false:

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());
    }
} 

我还写了一个展示我的问题的观点:

@model MvcApplication10.Models.Sample

A: @Html.IsRequired(m => m.A), B: @Html.IsRequired(m => m.B)

我原本希望这会打印A: false, B: true,但实际上会打印A: true, B: true

有没有办法让这个打印成为我预期的结果? IsRequired似乎总是返回true,即使我没有明确设置RequiredAttributedocs表示默认情况下对于非可空值类型是正确的。为什么没有简单的方法将它设置为假,就像我们可以通过验证一样?

编辑:我可以写一个像这样的自定义提供商,但我想知道是否有一个“简单”的方法:

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;
    }
}

6 个答案:

答案 0 :(得分:10)

如您所述,ValueTypes将默认为true。要解决此问题,如果类型为RequiredAttribute,您可以检查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))
{ ... }

答案 1 :(得分:4)

我猜你正面临一个MVC错误。无论如何,即使您使用

,必需将始终触发该情况
DataAnnotationsModelValidatorProvider
    .AddImplicitRequiredAttributeForValueTypes = false;

这已经讨论过here并报告了here。此示例更进一步,并显示当隐式必需触发器时,它不会阻止IValidatableObject执行。如果您从第二个链接运行演示,则可以重现您的案例,必要时始终如此。

无论如何,这很容易解决,因为如果你说A is obviously not required与它可以为空是一样的话,那就这样做吧:

public bool? A { get; set; }

答案 2 :(得分:3)

如果您正在使用EditorTemplate,就像我一样,您还需要再做一步:

var metaData = ModelMetadata.FromLambdaExpression(Model => Model, ViewData);

var required = metaData.ContainerType.GetProperty(metaData.PropertyName).GetCustomAttributes(typeof (RequiredAttribute), false).Any();

您需要从模型元数据中获取容器类型,以便检查特定属性的属性;否则,您只是检查属性的属性,而不是属性的属性

答案 3 :(得分:1)

以下将返回true或false,具体取决于您是否具有[Required]。

typeof(<YourModel>).GetProperty(<PropertyName>).GetCustomAttributes(typeof(RequiredAttribute), false).Any()

答案 4 :(得分:0)

我在几个地方遇到过这个问题,并实施了一个像这样的简单修复

if (metadata.ModelType == typeof(System.Boolean))
{
    metadata.IsRequired = false;
}

答案 5 :(得分:0)

如果您希望它也适用于可空类型:

private static bool RequiredAttrExists(ModelMetadata metaData)
{
    if(!metaData.ModelType.IsValueType && metaData.IsRequired)
        return true;
    else if (metaData.ModelType.IsValueType && metaData.ContainerType.GetProperty(metaData.PropertyName).GetCustomAttributes(typeof(RequiredAttribute), false).Any())
        return true;
    return false;
}