ayk
ayk

Reputation: 1497

Handling Data Annotation Validation with Custom ValidationResult

I have a custom ValidationAttribute like this :


public class ReceiverRegion : ValidationAttribute
    {
        private RegionService regionService;
        private CityService cityService;

        public ReceiverRegion() : base("Incorrect region code")
        {
            this.regionService = new RegionService();
            this.cityService = new CityService();
        }

        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            int regionId = Convert.ToInt32(value);
            int plate = Convert.ToInt32((validationContext.ObjectInstance as CorporateOrderItem).ReceiverCity);

            int productGroupId = Convert.ToInt32(validationContext.Items["productGroup"].ToString());

            if (!CheckReceiverRegionExistence(regionId, plate, productGroupId))
            {
                IEnumerable regions = this.regionService.GetList(cityService.GetByPlate(plate).PKCityId);

                return new CorporateOrderValidationResult(base.ErrorMessage, regions.Select(r=>r.Name));
            }
            return CorporateOrderValidationResult.Success;
        }

        private bool CheckReceiverRegionExistence(int plate, int regionId, int productGroup)
        {
            return !(regionService.GetByCityAndRegionIdForProductGroup(plate, regionId, productGroup) == null);
        }
    }

But as you can see in IsValid method, I'm trying to return a custom object which inherits from ValidationResult. My problem is, I can't access the extra members of my CorporateOrderValidationResult instance since IsValid returns the base ValidationResult type. Below is the code where I call validate and get a collection of ValidationResult as return value.


List results = new List();
bool isValid = Validator.TryValidateObject(instance, context, results, true);

I tried to cast results object to List<CorporateOrderValidationResult>, but no matter what I try(for instance --> item as CorporateOrderValidationResult or results.OfType<CorporateOrderValidationResult>() or (CorporateOrderValidationResult)item) I either get InvalidCastException or null value. Is there a possible way to convert this list of result to a list of my custom result class? Thanks...

Upvotes: 3

Views: 6156

Answers (2)

Kuba
Kuba

Reputation: 839

I know this is old question, but first result in Google when you ask for custom ValidationResult class. Example presented by Vyacheslav Volkov works, but why?

I spent few hours today trying to make my code working - the key line is:

public TestResult(): base ("test")

I was not going to use error message at all, I had my own structure for this so I wrote my ctor like this:

public TestResult() : base("") - empty string sent to base class has consequences, that are not mentioned in documentation. TryValidate() method will not insert our object into results collection as it is returned by our version of IsValid(), but will build new one with default error message for this field, like "Field " + fieldName + " is Invalid" and put it instead of the one we want.

Upvotes: 0

Vyacheslav Volkov
Vyacheslav Volkov

Reputation: 4742

I tried to reproduce this behavior, but everything works as expected:

public class TestClass
{
    [TestValidation]
    public string TestProp { get; set; }
}

public class TestValidationAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        return new TestResult();
    }
}

public class TestResult : ValidationResult
{
    public TestResult()
        : base("test")
    {
    }
}

Code for validation:

var instance = new TestClass();
var context = new ValidationContext(instance, null, null);
var results = new List<ValidationResult>();
bool isValid = Validator.TryValidateObject(instance, context, results, true);
//Contains one instance of TestResult
var customResults = results.OfType<TestResult>().ToArray();

Upvotes: 3

Related Questions