ASP.NET MVC: How to execute Data Annotation validations in the service layer?

╄→гoц情女王★ 提交于 2020-01-14 10:34:15

问题


In a recent question posed here: ASP.NET MVC: Is Data Annotation Validation Enough?

...it was concluded that relying on data annotation validation (triggered by the model binder) was not enough to ensure that validation was always executed. We still need to add the same validation logic in the services layer (or somewhere else after ModelBinding happens). The unfortunately thing about this is that we will be duplicating our validation code (once with Data Annotations and again in the services layer). Is there an easy way for the services layer to trigger validation based on what's been defined in Data Annotations? If this can be possible, then we will get the best of both worlds...we won't need to repeat the validation code, but we'll still ensure that the validation always gets executed.


回答1:


With the help of this blog: http://goneale.com/2009/03/04/using-metadatatype-attribute-with-aspnet-mvc-xval-validation-framework/ I was able to create a method that will test my object based on validations defined by data annotations. It will execute any validation attribute that derives from ValidateAttribute. I can now pass my object to this method from my service layer (or DomainModel) and my service layer is no longer dependent on the controller. This will ensure that validation will always be executed prior to persisting data into the database. I couldn't use the code on the blog as is, as I don't seem to have access to some of the extension methods that Graham was using, so here's my version of it:

    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 do and don't have buddy classes, although if you don't use buddy classes, this code can be simplified a bit. I hope you find this useful.




回答2:


Haven't you checked my answer in your previous question?
I submitted some code that does automatic validation based on DataAnnotation attributes of DTOs. As long as your DTOs are used in your Controller Action's parameters, they will get picked up by this attribute and validated no matter what.

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

  1. Do you write them yourself?
  2. Do you use EF or something similar?
  3. Do you auto generate them using some other technique (like T4)?

If you can control your DTO class generation, then you may as well add additiona interface to them. The code that I posted uses T4 over EF, xVal and DataAnnotation and custom interface that declares Validate() method that is implemented in each entity class.




回答3:


I wanted to achieve the same and tried Johnny's answer. It works fine until you have validations that are relative to other properties, like using the RequiredIf attribute.

I ended up with using the Validator class in System.ComponentModel.DataAnnotations which is actually ment for this and should apply the exact same full logic that's normally applied.

This is the example 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);
    }


来源:https://stackoverflow.com/questions/1562717/asp-net-mvc-how-to-execute-data-annotation-validations-in-the-service-layer

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!