How can I cleanly share a Custom Validator (ValidationAttribute) for a data model, a ViewModel, and a DTO in ASP.NET

I’m doing a ASP.NET MVC course. I’m building a REST Web API using ASP.NET WebAPI 2. The application also contains standard MVC 5 views. I’m using DTOs (Data Transfer Objects) to decouple the API from the data model. I’ve made a custom ValidationAttribute that I have applied to a property in my data model, and I’d like to use the same Validation attribute for a property on my DTO as well as a property ViewModel used in an MVC view.

This requires casting the ValidationContext.ObjectInstance to the right type. I have found a simple solution, but I don’t find it very elegant, and I’d like to know if there is a better way to do this.

The specific ValidationAttribute and property I’m talking about:

[Min18YearsIfAMember]
public DateTime? DateOfBirth { get; set; }

In the context of the solution (some details removed for brevity including CustomerViewModel):
public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public MembershipType MembershipType { get; set; }
    public byte MembershipTypeId { get; set; }

    [Min18YearsIfAMember]
    public DateTime? DateOfBirth { get; set; }
}    


public class CustomerDto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public byte MembershipTypeId { get; set; }

    [Min18YearsIfAMember]
    public DateTime? DateOfBirth { get; set; }  
}

public class Min18YearsIfAMemberAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        // Check it here
        var customer = validationContext.ObjectInstance as Customer;
        if (customer != null)
            return DoValidation(customer.MembershipTypeId, customer.DateOfBirth);

        // Check it here
        var customerVm = validationContext.ObjectInstance as CustomerViewModel;
        if (customerVm  != null)
            return DoValidation(customerVm.MembershipTypeId, customerVm.DateOfBirth);

        // Yes I should probably check it here too
        var customerDto = validationContext.ObjectInstance as CustomerDto;
            return DoValidation(customerDto.MembershipTypeId, customerDto.DateOfBirth);
    }

    private ValidationResult DoValidation( int membershipTypeId, DateTime? DateOfBirth)
    { 
        // Do the validation....
    }
}

It’s readable, but I find it ugly having to check each possible case like so ValidationContext.ObjectInstance as Customer.

Is there a better way?

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

In the data annotation attribute, you can specify the dependent property while attaching the attribute and using that you can validate the property for object types:

public class Min18YearsIfAMemberAttribute : ValidationAttribute
{
    private string _dependentProperty { get; set; }

    public Min18YearsIfAMemberAttribute(string dependentProperty)
    {
        this._dependentProperty = dependentProperty;
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var field = validationContext.ObjectType.GetProperty(_dependentProperty);
        if (field != null)
        {
            var dependentValue = (byte)field.GetValue(validationContext.ObjectInstance, null);
            
            return DoValidation(dependentValue, (DateTime?)value);
        }
        else
        {
            return new ValidationResult("<Your message here>");
        }
    }

    private ValidationResult DoValidation( int membershipTypeId, DateTime? DateOfBirth)
    { 
        // Do the validation....
    }

Now while attaching the attribute we specify the dependent property name [Min18YearsIfAMember("MembershipTypeId").
public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    public MembershipType MembershipType { get; set; }
    public byte MembershipTypeId { get; set; }

    [Min18YearsIfAMember(nameof(MembershipTypeId))]
    public DateTime? DateOfBirth { get; set; }
}


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x