如何禁用ModelMetadata.对于非空值类型总是为真

本文关键字:类型 空值 何禁用 ModelMetadata 于非 | 更新日期: 2023-09-27 18:16:26

我有一个简单的模型:

public class Sample
{
    public bool A { get; set; }
    [Required]
    public bool B { get; set; }
}

A显然不是必需的。因此,为了验证,我在Global.asax中设置了DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false

我也有一个简单的html帮助,打印真或假,如果模型是必需的:

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似乎总是返回真,即使我没有明确地设置RequiredAttribute。文档声明默认情况下对于非空值类型为真。为什么没有简单的方法将其设置为false,就像我们可以使用验证一样?

EDIT:我可以这样写一个自定义提供程序,但我想知道是否有一个"简单"的方法来解决这个问题:

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

如何禁用ModelMetadata.对于非空值类型总是为真

正如您注意到的,ValueTypes将默认为true。为了解决这个问题,如果类型是ValueType,您可以检查RequiredAttribute

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))
{ ... }

我猜你正面临MVC错误。Required总是在这种情况下触发,无论什么情况,即使你使用

DataAnnotationsModelValidatorProvider
    .AddImplicitRequiredAttributeForValueTypes = false;

这已经在这里讨论和报道过了。这个例子进一步说明,当隐式的required触发时,它不会阻止IValidatableObject的执行。如果你从第二个链接运行演示,你可以复制你的案例,在需要的地方总是为true。

无论如何,这很容易解决因为如果你说A is obviously not required等于说它是可空的,那么就这样做:

public bool? A { get; set; }

如果您像我一样在EditorTemplate中工作,则需要再执行一步:

var metaData = ModelMetadata.FromLambdaExpression(Model => Model, ViewData);
var required = metaData.ContainerType.GetProperty(metaData.PropertyName).GetCustomAttributes(typeof (RequiredAttribute), false).Any();

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

根据是否有[Required]返回true或false。

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

我在几个地方都遇到过这个问题,我已经实现了一个简单的修复,像这样

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

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

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