ASP.NET MVC: how to perform data annotation checks at the service level?

In a recent question asked here: ASP.NET MVC: Enough data annotation validation?

... it was concluded that relying on data annotation validation (caused by model binding) was not enough to ensure that validation was always performed. We still need to add the same validation logic at the service level (or somewhere else after the ModelBinding happens). Unfortunately, in this we will be duplicating our validation code (once with data annotations and again at the service level). Is there an easy way for the service layer to initiate validation based on what was defined in Data Annotations? If possible, then we get the best of both worlds ... we don't need to repeat the verification code, but we will still ensure that the verification is always done.

+2


source to share


3 answers


With this blog post: http://goneale.com/2009/03/04/using-metadatatype-attribute-with-aspnet-mvc-xval-validation-framework/ I was able to create a method that will validate my object based on validations defined by data annotations. It will execute any validation attribute that it gets from the ValidateAttribute. Now I can pass my object to this method from my service level (or DomainModel) and my service level is no longer dependent on the controller. This ensures that validation is always performed before storing data in the database. I couldn't use the code on the blog as it is as I don't have access to some of the extension methods that Graham used, so here's my version:

    public static IList<KeyValuePair<string, string>> GetErrors(object obj)
    {
        // get the name of the buddy class for obj
        MetadataTypeAttribute metadataAttrib = obj.GetType().GetCustomAttributes(typeof(MetadataTypeAttribute), true).FirstOrDefault() as MetadataTypeAttribute;

        // if metadataAttrib is null, then obj doesn't have a buddy class, and in such a case, we'll work with the model class
        Type buddyClassOrModelClass = metadataAttrib != null ? metadataAttrib.MetadataClassType : obj.GetType();

        var buddyClassProperties = TypeDescriptor.GetProperties(buddyClassOrModelClass).Cast<PropertyDescriptor>();
        var modelClassProperties = TypeDescriptor.GetProperties(obj.GetType()).Cast<PropertyDescriptor>();

        var errors = from buddyProp in buddyClassProperties
                           join modelProp in modelClassProperties on buddyProp.Name equals modelProp.Name // as this is an inner join, it will return only the properties that are in both the buddy and model classes
                           from attribute in buddyProp.Attributes.OfType<ValidationAttribute>() // get only the attributes of type ValidationAttribute
                           where !attribute.IsValid(modelProp.GetValue(obj))
                           select new KeyValuePair<string, string>(buddyProp.Name, attribute.FormatErrorMessage(string.Empty));

        return errors.ToList();
    }

      



This code works with both classes that have and do not have buddy classes, although if you are not using buddy classes this code can be simplified a bit. I hope you find it helpful.

+3


source


Did you not check my answer in your previous question?
I've posted code that does automatic validation based on the DataAnnotation attributes for a DTO. As long as your DTOs are used in your controller action parameters, they will be picked up by this attribute and validated no matter what.

The only question is, how do you generate your DTOs?



  • Do you write them yourself?
  • Are you using EF or something similar?
  • Do you automatically generate them with some other technique (like T4)?

If you can control your generation of DTO classes, you can add an additional interface to them as well. The code I posted uses T4 over EF, xVal and DataAnnotation and a custom interface that declares a method Validate()

that is implemented in each entity class.

0


source


I wanted to do the same and tried Johnny's answer. It works fine until you have validations related to other properties, such as using the RequiredIf attribute.

I ended up using the Validator class in System.ComponentModel.DataAnnotations, which is actually for this, and should apply the same complete logic that would normally apply.

This is a sample method that shows you how to do this with the Validator class.

    public static bool TryValidate(object obj, List<ValidationResult> results = null)
    {
        var context = new ValidationContext(obj, serviceProvider: null, items: null);
        return Validator.TryValidateObject(obj, context, results, true);
    }

      

0


source







All Articles