4

I am creating a example for better understanding.

[CustomValidator("Property1","Property2", ErrorMessage= "Error1")]
[CustomValidator("Property3","Property4", ErrorMessage= "Error1")]
public class MyViewModel
{
    public string Property1 {get; set;}
    public string Property2 {get; set;}
    public string Property3 {get; set;}
    public string Property4 {get; set;}
}

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class CustomValidator : ValidationAttribute
{
    All the required stuff is written.
}

Only the second validator (or the last one in the list) gets fired and ignores the first one. I am not sure if this is the right approach for this scenario. Any suggestions?

2
  • Did you find a solution for this? I've got the same issue. I don't want to implement IValidatableObject because I need to use a MetadataType on multiple classes. Commented Jun 17, 2011 at 17:17
  • Found the solution, see my answer. Commented Jun 17, 2011 at 17:31

4 Answers 4

1

if you are using Linq to SQL why not try something like this

add a rule violations class to handle rule violations

public class RuleViolation
{
    public string ErrorMessage { get; private set; }
    public string PropertyName { get; private set; }
    public RuleViolation(string errorMessage)
    {
        ErrorMessage = errorMessage;
    }
    public RuleViolation(string errorMessage, string propertyName)
    {
        ErrorMessage = errorMessage;
        PropertyName = propertyName;
    }
}

now on your data class

[Bind(Exclude="ID")]
public partial class Something
{
    public bool IsValid
    {
        get { return (GetRuleViolations().Count() == 0); }
    }

    public IEnumerable<RuleViolation> GetRuleViolations()
    {
        if (String.IsNullOrEmpty(Name.Trim()))
            yield return new RuleViolation("Name Required", "Name");
        if (String.IsNullOrEmpty(LocationID.ToString().Trim()))
            yield return new RuleViolation("Location Required", "LocationID");
        yield break;
    }

    partial void OnValidate(ChangeAction action)
    {
        if (!IsValid)
            throw new ApplicationException("Rule violations prevent saving");
    }

}

and in your controller's methods for updating, use the updatemodel method for changing properties

Something something = somethingRepo.GetSomething(id);
try
{
    //update something
    UpdateModel(something);
    somethingRepo.Save();
    return RedirectToAction("Index");
}
catch
{
    ModelState.AddRuleViolations(something.GetRuleViolations());
    return View(something);
}

this way you can just add rules to your data class as it changes and it will be reflected in your updates, etc

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your suggestion. I use the entity framework, and i have a backup to validate this (similar to your suggestion). But i want to catch these errros at the view model. To be more precise, Property1 and Property 2 are of type decimal and would like to make sure one is greater than the other. I just can't get all the validators to fire, if multiple are defined on a class, only the last one works.
1

I found another question that answers this. You have to override Attribute.TypeId.

Custom validation attribute with multiple instances problem

Comments

0

you dont really need all that code use data annotations by creating a metaData class for your model link text

that should set you on the right road also read up on html helpers and buddy classes ( this is what they call em)

1 Comment

Well, what i am doing is simialr to what you say, but at the class level instead of properties. I have to compare 2 properties, so i had to create a Custom class to compare.
0

I had the same problem.

I come up with the following solution.

Your POCO class might implement interface IValidatableObject.

This requires from you to implement the following method.

    public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        // return list of appropriate ValidationResult object            
        var customResult = new List<ValidationResult>();
        customResult.Add(new ValidationResult("message", new List<string>(){"Property1"});
    }

You can place any validation logic there. This has also the advantage over class-level attribute. Class-level attribute can only be shown in ValidationSummary (they are not related to any property). In contrast to it you can set specific members while returning ValidationResult. This allows to show validation information beside specific control to which the message concerns.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.