我如何在来自资源的 DataAnnotations 错误消息中使用 DRY(不要重复自己)

How could i use DRY(don't repeat yourself) in DataAnnotations Error messages form Resourse

这是我的代码

    [Required(ErrorMessageResourceType = typeof(DCC.RegistrationVMLiterals), ErrorMessageResourceName = "Required")]
    [RegularExpression("^[0-9]*$", ErrorMessageResourceType =typeof(DCC.RegistrationVMLiterals),ErrorMessageResourceName ="MustBeNumber")]
    [Display(Name = "BeneficiaryNo")]
    public long BeneficiaryNo { get; set; }

    [Required(ErrorMessageResourceType = typeof(DCC.RegistrationVMLiterals), ErrorMessageResourceName = "Required")]
    [RegularExpression("^[a-zA-z]*$",ErrorMessage ="must only be letters")]
    [Display(Name = "FullName")]
    public string FullName { get; set; }
    [Required(ErrorMessageResourceType = typeof(DCC.RegistrationVMLiterals), ErrorMessageResourceName = "Required")]
    [Display(Name = "GenderID")]
    public int GenderID { get; set; }`

试试这个,还没有测试过,但应该可以:

[AttributeUsage(AttributeTargets.Class)]
public class AggregatedRequiredAttribute: ValidationAttribute
{
    private readonly string[] _propertiesToValidate;
    private readonly string message = Resources.ValRequired;

    public AggregatedRequiredAttribute(params string[] propertiesToValidate)
    {
        if (propertiesToValidate == null || !propertiesToValidate.Any()) throw new ArgumentException(nameof(propertiesToValidate));
        _propertiesToValidate = propertiesToValidate;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (!validationContext.ObjectType.GetMember(validationContext.MemberName).Any())
            throw new InvalidOperationException("Current type does not contain such property.");

        if (!_propertiesToValidate.Contains(validationContext.MemberName))
            return ValidationResult.Success;

        var defaultRequired = new RequiredAttribute() {ErrorMessage = message};
        return defaultRequired.IsValid(value) ? ValidationResult.Success : new ValidationResult(message);
    }
}

然后你可以像这样使用它(来自默认 MVC 模板的 ViewModel):

[AggregatedRequired("Email","Password")]
public class RegisterViewModel
{
    [EmailAddress]
    [Display(Name = "Email")]
    public string Email { get; set; }

    [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }
}

注意,我只使用了一次错误消息字符串,您也可以在错误消息中使用一些字母并将其动态替换为正在验证的 属性 的实际名称。

更新

我不确定第一个 if 语句,你应该测试一下。

更新 2

另外我想我用错了IsValid方法,但思路本身很清楚。

我认为你误解了这个概念或 DRY。

你在重复的事实

[Required(ErrorMessageResourceType = typeof(DCC.RegistrationVMLiterals), ErrorMessageResourceName = "Required")]
[RegularExpression("^[0-9]*$", ErrorMessageResourceType =typeof(DCC.RegistrationVMLiterals),ErrorMessageResourceName ="MustBeNumber")]

在此上下文中不是重复。

重复会是这样的:

public void DoSomething()
{
    string myString = "ABC";
    Print(myString);
    Do something...
}

public void DoSomething1()
{
    string myString = "ABC";
    Print(myString);
    Do something different...
}

对此的 DRY 响应是

public void DoSomething()
{
    Print(getString());
    Do something...
}

public void DoSomething1()
{
    Print(getString());
    Do something different...
}

public string getString()
{
    return "ABC";
}