在运行时根据另一个容器属性在服务器端验证时添加ValidationAttribute

时间:2012-08-15 19:28:15

标签: asp.net asp.net-mvc-3 data-annotations

有一个类(精简版):

public class Parameter
{
    public Guid Id { get; set; }

    public decimal Value { get; set; }
}

在我看来,我有一个参数字典,它们会像这样呈现:

<input ... name="Parameters[3].Key" type="hidden" value="UniqueParamName" />
<input ... name="Parameters[3].Value.Id" type="hidden" value="395816ad-dfde-11e1-8c36-848f69f05f09" />
<input ... name="Parameters[3].Value.Value" type="text" value="75" />

验证规则将由用户通过某些GUI设置并存储在数据库中。 我想通过ValidationAttributeParameter.Id获取当前Parameter.Value的{​​{1}},但我看不到任何方法。 我还尝试在ModelMetadataProvider.CreateMetadata中添加属性,其中容器可以通过某些[讨厌的]反射来访问,但是当为“值”创建元数据时尚未分配“Id”(无论如何,它不是可靠)。 也许我过分复杂了整个事情,我只想坚持内置验证。

public class ExtendedValidationProvider : DataAnnotationsModelValidatorProvider
{
    protected override IEnumerable<ModelValidator> GetValidators(
        ModelMetadata metadata, 
        ControllerContext context, 
        IEnumerable<Attribute> attributes)
    {
        if (metadata.ContainerType == typeof(Parameter) 
            && metadata.PropertyName == "Value")
        {
            Guid parameterId = <some magic code>;
            IEnumerable<Attribute> validationAttributes 
                = db.GetValidationAttributesByParameterId(parameterId);
            return base.GetValidators(metadata, context, validationAttributes);
        }

        return Enumerable.Empty<ModelValidator>();
    }
}

2 个答案:

答案 0 :(得分:0)

在我看来,您应该使用模型验证。模型验证将根据您的模型和应用模型中定义的验证属性的规则进行验证。

public class MyModel
{
     // This model uses model validation

     [Required(ErrorMessage = "FirstName is a required field.")]
     [StringLength(50, ErrorMessage = "Your FirstName can not exceed 50 characters.")]
     public string FirstName { get; set; }

     [Required(ErrorMessage = "LastName is a required field.")]
     public string LastName { get; set; }
}

这会设置您的模型以进行验证。然后在您的控制器/操作中,您需要使用 ModelState.IsValid 验证您的ModelState。您也可以在控制器中进行自定义“验证”,并使用 ModelState AddModelError 添加自定义错误。

如果您想更进一步并创建自己的验证属性,那么您需要做的就是创建一个继承自 ValidationAttribute 类的新类。这样您就可以根据自己的DAL图层值或与模型绑定的其他值进行验证。

如果您需要使用全球化,使用模型验证还可以很好地(轻松地)为您设置,因为有可用的属性可以让您选择资源文件,其中包含您可能已定义的各种语言的资源字符串。

我是从记忆中做到这一点,但这应该引导你朝着正确的方向使用MVC3的内置功能,并利用模型绑定和模型状态。

HTH

答案 1 :(得分:0)

我想我可以在自定义ModelMetadataProvider中设置容器,如下所示:

public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor,
    Type containerType, string propertyName)
{
    var metadata = _InnerProvider.GetMetadataForProperty(modelAccessor, containerType, propertyName);
    if (containerType == typeof(Parameter) 
        && !metadata.AdditionalValues.ContainsKey("container"))
    {
        metadata.AdditionalValues["container"] = GetContainer(modelAccessor);
    }

    return metadata;
}

GetContainer是一种使用[讨厌]反射的方法(实际上是编译和缓存的表达式)。

最后,验证提供者方没有魔法代码:

public class ExtendedValidationProvider : DataAnnotationsModelValidatorProvider
{
    protected override IEnumerable<ModelValidator> GetValidators(
        ModelMetadata metadata, 
        ControllerContext context, 
        IEnumerable<Attribute> attributes)
    {
        if (metadata.ContainerType == typeof(Parameter) 
            && metadata.PropertyName == "Value")
        {
            Guid parameterId = ((Parameter)metadata.AdditionalValues["container"]).Id;
            IEnumerable<Attribute> validationAttributes 
                = db.GetValidationAttributesByParameterId(parameterId);
            return base.GetValidators(metadata, context, validationAttributes);
        }

        return Enumerable.Empty<ModelValidator>();
    }
}