0

here is my code

    [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; }`
Sir Rufo
  • 18,395
  • 2
  • 39
  • 73
  • You can write custom class-scope ValidationAttribute, which can accept in constructor, for instance, property names to be checked. Please, see code sample [here](http://stackoverflow.com/questions/3880727/class-level-validation) – Ryan Apr 23 '17 at 10:23
  • if u see the simple code i posted im using the errormessage resource type for the three,, how can i not repeat myself – Moha Yusuf Apr 23 '17 at 11:30
  • I will show you. – Ryan Apr 23 '17 at 11:34

2 Answers2

0

Try this one, have not tested, but this should work:

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

Then u can use it like this (ViewModel from default MVC template):

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

Notice, that i used error message string only once, also u can use some letter in error message and replace it dynamically to actual name of property being validated.

UPDATE

I am not sure about first if statement, you should test it.

UPDATE 2

Also i guess i used wrong IsValid method, but idea itself is clear.

Ryan
  • 609
  • 6
  • 13
0

I think you are misunderstanding the concept or DRY.

The fact you are repeating

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

Isn't a Repeat in this context.

A repeat would be something like:

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

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

The DRY response to this would be

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

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

public string getString()
{
    return "ABC";
}
Andrew Harris
  • 1,419
  • 2
  • 17
  • 29
  • Personally I think your code is fine. If you want to abstract the repetition of annotations your code will become a lot more complicated than it needs to be for no benefit in this instance. – Andrew Harris Apr 23 '17 at 12:04
  • @AndrewHarris, anyway this is kinda repetition problem, so if you have a huge domain model u can simplify work for yourself a little bit. – Ryan Apr 23 '17 at 12:08
  • @Ryan I disagree. DRY is about addressing the issue where a repeated code block is updated in one place and not another (as one example). Each field here is and should be defined in its own right. – Andrew Harris Apr 23 '17 at 12:14
  • @AndrewHarris, entirely agree with you. Actually, for current particular case my solution looks like overengineering, more over in huge projects this is a matter of tradeoff: to apply attribute to a class, or just copy-paste the same code many times. But solution has right to live, so no point to argue. – Ryan Apr 23 '17 at 12:30
  • In large projects where you want to reuse things like regex, apply logging to attribute and a few other things I would absolutely agree that you can do this, but for the most part its not addressing the problem DRY looks to prevent (exception being the repeating regex pattern). – Andrew Harris Apr 23 '17 at 12:34