1

Hi I have found this answer already: MVC3 Validation - Require One From Group

Which is fairly specific to the checking of group names and uses reflection.

My example is probably a bit simpler and I was just wondering if there was a simpler way to do it.

I have the below:

public class TimeInMinutesViewModel {

    private const short MINUTES_OR_SECONDS_MULTIPLIER = 60;

    //public string Label { get; set; }

    [Range(0,24, ErrorMessage = "Hours should be from 0 to 24")]
    public short Hours { get; set; }

    [Range(0,59, ErrorMessage = "Minutes should be from 0 to 59")]
    public short Minutes { get; set; }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public short TimeInMinutes() {
        // total minutes should not be negative
        if (Hours <= 0 && Minutes <= 0) {
            return 0;
        }
        // multiplier operater treats the right hand side as an int not a short int 
        // so I am casting the result to a short even though both properties are already short int
        return (short)((Hours * MINUTES_OR_SECONDS_MULTIPLIER) + (Minutes * MINUTES_OR_SECONDS_MULTIPLIER));
    }
}

I want to add a validation attribute either to the Hours & Minutes properties or the class itself.. and the idea is to just make sure at least 1 of these properties (Hours OR minutes) has a value, server and client side validation using a custom validation attribute.

Does anyone have an example of this please?

Thanks

Community
  • 1
  • 1
Pricey
  • 5,799
  • 12
  • 60
  • 84
  • Possible duplicate of [Data Annotations for validation, at least one required field?](https://stackoverflow.com/questions/2712511/data-annotations-for-validation-at-least-one-required-field) – KyleMit Mar 30 '18 at 13:01

2 Answers2

5

Check out FluentValidation http://fluentvalidation.codeplex.com/ or you can use this little helper for every ViewModel you wanna check if at least one property has value, or modify it further for your needs.

public class OnePropertySpecifiedAttribute : ValidationAttribute
{       
    public override bool IsValid(object value)
    {            
         Type typeInfo = value.GetType();
         PropertyInfo[] propertyInfo = typeInfo.GetProperties();
         foreach (var property in propertyInfo)
         {
            if (null != property.GetValue(value, null))
            {                    
               return true;
            }
         }           
         return false;
    }
}

And apply it on your ViewModel:

[OnePropertySpecified(ErrorMessage="Either Hours or Minutes must be specified.")]
public class TimeInMinutesViewModel 
{
  //your code
}

Regards.

Matija Grcic
  • 12,963
  • 6
  • 62
  • 90
2

The example you linked to defines the group by applying attributes to the properties, which gives a lot of flexibility. The cost of that flexibility is the reflection code. A less flexible approach would be simpler to implement, but it would be more narrowly applicable.

Here's an IsValid method for such an approach; I'll leave it to you to adapt the rest of the other example:

protected override ValidationResult IsValid(object value, ValidationContext validationContext) 
{
    var viewModel = value as TimeInMinutesViewModel;
    if (viewModel == null)
    {
        //I don't know whether you need to handle this case, maybe just...
        return null;
    }

    if (viewModel.Hours != 0 || viewModel.Minutes != 0)
        return null;

    return new ValidationResult(FormatErrorMessage(validationContext.DisplayName)); 
} 
phoog
  • 42,068
  • 6
  • 79
  • 117
  • Hi thanks for the quick reply, can I just ask that in your example here I assume your using the attribute on the class due to this line: "var viewModel = value as TimeInMinutesViewModel". Would this same thing be difficult to achieve when only applying the attribute to each property? thanks – Pricey Apr 14 '12 at 21:45
  • @Pricey Yes, this answer assumed that the attribute is applied to the class. And obviously, this attribute can only be applied to *one* class. Like I said, it's inflexible and narrow. To do this with an attribute on two properties, you'd either have to use the reflection-based approach (which is what I'd do, frankly) or, assuming you can get the information you need from the `ValidationContext`, write a narrowly-specialized attribute to apply to the properties. Unfortunately, I don't know enough about the ValidationContext to know whether that's feasible. – phoog Apr 14 '12 at 22:41