A: 

Wait for MVC 3. I'm being serious.

The options for class wide custom validation attributes are pretty poor right now without any really good extensibility points. Your pretty much stuck creating a custom ModelBinder, which can then add values to ModelState, to do anything complex with validation attributes.

Use the attributes just like you would and then detect what Types are requested from the binder, Reflect to find the attributes, and then validated/add to model state as necessary.

MVC 3 fixes this problem but until then your stuck creating your own binder.

jfar
This is unrealistic. I understand you're point: that the validation extensibility in MVC 2 is lagging in maturity behind MVC itself; however, MVC 3 is .Net 4.0 only and uses a ton of new conventions as far as syntax goes. There's not much of a business case on redoing this project in MVC 3 when it's not even out yet and the benefits are minimal considering this project is very close to production release. Still, I appreciate your comment. Thank you for taking the time to give your feedback.
Jason
@Jason? Well check out, http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/03/17/a-better-model-binder.aspx, and then see if that changes your mind. The attributes are nice but there are other ways to accomplish your goal. I know "wait" isn't a practical answer but its the only one which correctly answers what your looking for.
jfar
-1 for doing validation in a model binder. IMHO validation is an aspect of the application that should be independent. Suggesting to use reflection and adding manually to the model state means that your validation logic is tied to the MVC infrastructure and not reusable. Also you risk up having parts of the validation in the model binder and other parts as data annotation attributes, etc... which is not very maintainable solution. Also in the example you showed, no validation is performed, it also states in the first paragraph the role of a model binder. No validation there.
Darin Dimitrov
@Darin Dimitrov, lol, you know MVC does ModelValidation in the model binder? In fact this is considered a feature. "Downvoted for using the same technique the framework uses". - "IMHO validation is an aspect of the application that should be independent" thats a bad answer. Validating the user entered a int into a textbox is not part of independent business logic but directly tied to the underlying UI platform.
jfar
I upvoted jfar's response b/c I think his explanation holds pretty well. I agree with trying to separate the concerns and really would rather not mix my validation logic with a custom model binder, but after doing a test app, I know I can at least get all of the properties of an object in the BindModel method (and the SetProperty method provided I add logic to ensure both properties have been validated).
Jason
Please see the updated question. It explains the issue I am having more clearly.
Jason
A: 

You could use a class attribute to achieve this:

[AttributeUsage(AttributeTargets.Class)]
public class RatingValidation : ValidationAttribute
{
    public override bool IsValid(object value)
    {
        var model = (MyModel)value;
        // TODO: here you have the model so work with the 
        // Rating and Heading properties to perform your 
        // validation logic

        return true;
    }
}

And you model:

[RatingValidation]
public class MyModel
{
    public String Heading { get; set; }
    public int Rating { get; set; }
}

Or even better: use FluentValidation which integrates nicely with ASP.NET MVC.

Darin Dimitrov
I initially started with this approach. It has 2 problems. First of all, it seems like only 1 validation error can be displayed at a time. An example would be, say Rating's value wasn't b/t 1 and 5, but the user didn't put a value for some other required property in the model. I could check both here, but only display a single message. Furthermore, handling the actual displaying of that message inside the custom control finicky. I can post code to illustrate what I mean.
Jason
@Jason, yes this allows a single validation message to be displayed but for the combined property validation. You can still place validation attributes on individual properties like `[Required]` to validate each property separately. You can also have multiple class validation attributes which will allow you to validate different combinations of dependent properties with different error messages. [FluentValidation](http://fluentvalidation.codeplex.com/) is still worth checking out though.
Darin Dimitrov
Jason
Yes, validation kicks in during model binding. *I'm thinking I'm going to be stuck creating a custom ModelBinder* : hell no, if you had to write a custom model binder every time you needed to validate two dependent properties on a view model, ASP.NET MVC usefulness would be extremely limited to some *Hello World* applications which of course is not the case. I cannot even imagine writing a complex application with many business rules (and I have written some). Btw, did you have a look at [FluentValidation](http://fluentvalidation.codeplex.com/) (maybe the third time I am asking)?
Darin Dimitrov
@Darin Dimitrov - You know it takes just the very small amount of code to create a custom model binder right? Throw in a little infrastructure ( http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/03/17/a-better-model-binder.aspx ) and your writing the same amount of code per model binder as you would with FluentVal.
jfar
Voted down because the questioner isn't asking for something he already knows: "I can write a custom validation class that validates this object as a whole, but the problem". This answer has the same problem.
jfar
@jfar, I you shouldn't write a single class validation attribute that validates everything. You should write multiple attributes each validating different aspects of the model. I know that writing a model binder you would do the same amount of code. This is not the problem here. The problem is that model binders are useful for as their suggests binding data from the request to the model and invoking your validation logic. You shouldn't mix aspects. Also if you perform the validation in a model binder you will validate the object as a whole. Or you risk ending up doing validation in many places
Darin Dimitrov
--Continued. IMHO the validation logic should be centralized in a single location/part of the application for better maintainability.
Darin Dimitrov
@Darin Dimitrov, you should have said something like your last comment in your answer. Your answer still 1. Tell the questioner something he already knows. and 2. Tells the user to use a technique with issues he is trying to avoid.
jfar
@Darin, I have looked into the Fluent Validation you linked. It looks cool, but I fail to see how it solves this issue. I need a separate property value to put into the error message for a given property should its validation check fail. I didn't see an example of this. Do you know of one?
Jason
Please see the updated question. It explains the issue I am having more clearly.
Jason
A: 

Better phrasing of this question altogether:

How can I get a distinct error message to show for the validation summary for a given form field with a separate & distinct message showing beside the field for which the validation applies?

Example:

Field: Rating
Summary Message: "Rating for Technical Knowledge is required and must be between 1 and 5." Field Validation Message: "*"

Hopefully that illustrates what I'm trying to do. From the research the answer seems to be this isn't possible with the current data annotations framework without rolling at least one html helper on my own: either create my own ValidationSummary or my own ValidationMessage helper...

Jason
A: 

I wanted to elaborate on jfar's suggestion. I am not agreeing that I need to wait till MVC 3; however, his point for creating a custom model binder is actually THE ONLY way to do what I am trying to do. I've tried both field/property level attributes and class level attributes. Neither of these are sufficient for the sort of messages I need.

For example, recall that my situation is that I have a template control that is strongly typed against a model with properties Index, Heading, & Rating. So a page of these controls displayed would be something like:

Heading: Technical Knowledge Rating: [text box]

Heading: Leadership Rating: [text box]

Heading: Work Ethic Rating: [text box]

...etc, etc.

For my validation messages, I wanted something very customized. It turns out my needs were too specific for the out of the box validating in MVC 2. I needed error messages for the Rating field to reference the Heading that Rating is associated with. So the validation would be something like:

The Rating for Work Ethic is required and must be a number between 1 and 5. The Rating value of 'asdf' for Work Ethic is not valid.

The issue with field level attributes is that they didn't have access to the Heading value. Furthermore, each object actually contains an IsEditable field and should that field's value be false, I bypass validations altogether.

The issue with class level attributes is two fold. For one, I can't control the key used in the ModelStateCollection. The default is the class name and index of the object on the page. This yields a result akin to: PersonRating[0], PersonRating[1]. The problem with this is that it means you can only have 1 error message at a class level validation. If you have 2 class level attributes, they both get put into the ModelStateCollection with the same key. I'm not really sure how that works as I wouldn't think a dictionary would let you do that. Perhaps it fails silently or the second message just overwrites the first. In addition to this, I still need the fields themselves to have their css changes to denote an error. With class level validations, I could not figure out how to do this because the key does not reference a field...so I have no idea which message goes with what field unless I do hard string checks, which seems a really bad solution.

The blog post I referenced earlier does provide a solution, but it requires far too much code and effort per attribute and just seemed like overkill.

In addition to all of this, MVC data annotations has basic validations hard wired into it to sort of save you from yourself. This includes invoking a Required validator if a type on your model is non-nullable. The internal MVC validations also do data checking to ensure you're not trying to submit a string value to a int type. In both of these cases, I didn't not see a viable way to correct the validation message without doing hard checks against the validation text.

So, in the end, I wrote my own model binder for this object. While this is not the recommended way, I take comfort in that this really is the only viable solution in my case due to the internal validations hardwired in the MVC framework. My model binder looked something like below:

        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            Object o = base.BindModel(controllerContext, bindingContext);
            string ratingKey = bindingContext.ModelName + ".Rating";            
            PersonRating pr = (PersonRating)o;
            ValueProviderResult ratingVpr = controllerContext.
                                        Controller.
                                            ValueProvider.
                                                GetValue(ratingKey);
            String ratingVal = ratingVpr.AttemptedValue;
            String ratingErrorMessage = getRatingModelErrorMessage(
                                            ratingKey,
                                            ratingVal,
                                            pr);

            if (!String.IsNullOrEmpty(ratingErrorMessage))
            {
                bindingContext.ModelState[ratingKey].Errors.Clear();
                bindingContext.ModelState.AddModelError(ratingKey, ratingErrorMessage);
            }

            return o;

                         }

The getRatingModelErrorMessage method is a custom method that performs validations on the Rating field of the PersonRating object and returns a string that represents the error message. If the string is null, no error was returned by the getRatingModelErrorMessage method.

I'll be the first to admit this isn't great code. There's always room to improve. Still it gets the job done. It's also worth noting that in situations where a value such as a text value is submitted for a non compatible type on the model, such as int, it will not be bound to the model. I am getting that value from the FormCollection via it's key.

Let me know if anyone has any suggestions on other ways this could be done or comments on the code in general. I'm always open for constructive criticism.

Jason