views:

62

answers:

2

All examples that I can find do something like this:

[Required]
public string Title { get; set; }

That's great for simple cases, but what about something that checks the database or something else server side?

For example, say I have a movie database and I want to allow people to rate it. How could I tell if someone has already rated a movie, as I'd only want them to rate a movie once.

I would think it would be something like:

public IEnumerable<string> ValidateUserHasNotAlreadyRatedMovie(User currentUser, Guid movieId)
{
  if(movieHasAlreadyBeenRated)
  {
    yield return "Movie been rated man!";
  }
}

Now then I'd call this with something like:

var errors = new List<string>();
errors.AddRange(ValidateUserHasNotAlreadyRatedMovie(topic, comment));
if(errors.Any())
{
  throw new SomeTypeOfCustomExtension??(errors);
}

Do I just need to extend Exception for a custom SomeTypeOfCustomExtension above, or is there something already built? Am I doing this the ASP.NET MVC 2 way?

After that how do I put it into the model state and let the view know something's wrong?

A: 

The ASP.NET 2.0 MVC way of doing custom validation is described here. It basically shows how to write a custom validation attribute. Here is an example of one such custom attribute that checks the database for name uniqueness:

public class UniqueNameAttribute : ValidationAttribute 
{ 
    public override bool IsValid(object value) 
    { 
        string str = (string)value; 
        if (String.IsNullOrEmpty(str)) 
            return true; 

        using (XDataContext vt = new XDataContext()) 
        { 
            return !(vt.Users.Where(x => x.Username.Equals(str)).Any()); 
        } 
    } 
} 

The ASP.NET 1.0 way (that doesn't require custom attributes) is described here.

Robert Harvey
The "ASP.NET 2.0 MVC way" you linked was precisely the article that does not explain how to do this, this is more complicated than decorating a property.The 1.0 way looks closer but there's a "The RuleViolation class is a helper class we'll add to the project that allows us to provide more details about a rule violation." So they don't already have a helper class in MVC 2 that does more than what is described in this article? I am not using L2S so I think I'm going to have to have an input model dto with a IsValid property that somehow encapsulates if each of the property decorators and
rball
the custom logic. Either way, thanks for the response, but not the answer I guess I was looking for. I've already done the Google searches for this and haven't found anything helpful.
rball
The first comment actually gets to this and then a response seems to help: public class UniqueNameAttribute : ValidationAttribute{public override bool IsValid(object value){string str = (string)value;if (String.IsNullOrEmpty(str))return true;using (XDataContext vt = new XDataContext()){return !(vt.Users.Where(x => x.Username.Equals(str)).Any());}}}
rball
I was thinking about it again and this still doesn't help. I mean, what property would I put this attribute on? Would I be putting it directly on the class itself somehow? It doesn't relate to any specific property, but more of a process on if the data can be saved.
rball
@rball: That's why I like the MVC 1.0 way of validating things like this. The validation is called when the form is posted, and you can write whatever validation logic in there that you want. Although people like Scott Hanselman dislike it (because it's kind of cut and pastey), it is actually quite effective for scenarios like this, because it encapsulates all of the validation logic in your ViewData object.
Robert Harvey
Well what I wonder is is within my input DTO I can have all of the attributes like [Required] and then have a IsValid method on it as well. From there I'll check each of the attributes and then tack on the process validators at the end. From there I'll return the custom exception, then in my controller I'll loop that to add to model state and then maybe I should be good to go. I just can't believe no one has a great example on doing this with proper SOC. I have a ton of places where I need to have some sort of checking like this. I guess maybe I'll update the question with what I come up
rball
with....................
rball
The ViewData object is what provides your Separation of Concerns. It is the glue that binds the View to the Controller method, and to your repository, if necessary. If you need to go back to the database to validate, there is really no way around that. Add a helper method to your repository to help accomplish the validation, if needed, and pass the result to your ViewData object.
Robert Harvey
The ViewData object enforces SOC by keeping the repository reference out of your view, and since it's a specialized object (i.e. you'll only be using it in one place), there really shouldn't be a concern about further SOC.
Robert Harvey
So I should have another property on the ViewData object (what I called the input DTO) like IsRated and then slap the attribute on that? Thanks for sticking in there with me :)
rball
Using the MVC 1.0 way, you would not use an attribute for the custom validation. Instead, you would populate a RuleViolation object with the custom validation error, and then use that to notify the user of the error on postback. I don't see any reason why you couldn't combine the two approaches, using attributes for simple validation like "required" fields, and a RuleViolation object for the more complex validations.
Robert Harvey
One last thought: Don't be too rigid about the rules for things like Separation of Concerns. Rules like that are meant to be followed until they no longer serve you well in a particular situation, at which time the rules are meant to be broken.
Robert Harvey
+1  A: 

See this it may help

Remote Validation with ASP.NET MVC 2

http://bradwilson.typepad.com/blog/2010/01/remote-validation-with-aspnet-mvc-2.html

Tassadaque
I think this is more geared to the client side calling the server side for an ajax type remote validation.
rball
@rball: For the record, this is probably the best way to do it. It allows you to immediately confirm that the movie rating is valid, without requiring a full re-render of the entire page. +1
Robert Harvey
Ok, I'll re-read this and give it a try. This crap sure seems hard!
rball
Still seems to have the attribute required on the property. I feel like I'm still missing something. I think I need to just incorporate what I have above and then loop through in the controller and tack on the extra errors to the model state. That should do it...I just thought there was a better way.
rball