Data Annotation

May 4, 2010 at 1:45 AM
Edited May 4, 2010 at 8:19 PM

I'm enjoying the tutorial and learning quite a bit about MVC 2 and ASP.NET's MVC implementation.

As for the tutorial.  I've stepped through most of it with complete success (and hopefully some understanding), up until now and the Data Annotation's section (p. 60).

I've added the code for the partial Album class and the metadata class.  This all makes sense, I think I understand what it's supposed to do, however, I think I missed something.

The "create" form looks fine when displayed.  To check the data annotation code, I simply press "Create" with no Album title, nor a price (still 0).  I then get an ConstraintException.  I'm assuming that this is because the album title is empty and the title field in the album table does not allow "NULL".  This is caught in the generated code in StoreDB.Designer.cs.  When I ignore the exception, I then get to the form with the errors highlighted in red with the annotations as expected.

As a further test, I entered a title, but no price.  This time there's no ConstraintException, however, the price validation is not caught and the record is created.

As yet another test, I changed the display names for the drop down lists to see if there would be a change on the form.  There was none.

Does anyone have any thoughts regarding what I might have missed?  Everything compiles just fine, and so far everything up to this point in the tutorial is working just fine as well.

Thanks in advance.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Update: It seems as if the code is first going to the main (generated) MvcMusicStore.Models.Album class before the partial class containing the metadata album class.  The above ConstraintException occurs on the simple assignment of the textbox to the title member variable.

How does one make sure that the partial class is checked first so that the fields are validated before the actual object.

OR

Does one turn off the offending exceptions somehow (seems like a bad idea).

Bill

May 6, 2010 at 2:45 AM

I have the exact same question. Why do we have to rely on the database throwing exceptions for validation?

ASP.NET Web Forms has a property called Page.IsValid, which you add to the Button handler in code behind. Is there something like Page.IsValid in MVC?

May 10, 2010 at 6:31 AM

@aboreham: there is ModelState.IsValid - which is somewhat similar. I think the important distinction is that Web Forms adds a lot of "automagic" to validate fields on the page. MVC is much cleaner in separating client validation from server validation (of course, you can validate specific fields in AJAXy way, if you want).

The tutorial doesn't go into client validation which is generally done through JavaScript (MVC project includes jquery.validate that does most of validation coding for you); therefore Button handler is irrelevant here. You click the button before server-side validation happens.

@billbris: You shouldn't be getting ConstraintException... it means that you got all the way to the database; and you should only get to the data annotation layer in the model. The second case - where the record was created confirms that the attributes on the data annotation class do not kick in!

May 10, 2010 at 7:31 AM

Don't you still need server side validation in case javascript is turned off?

May 10, 2010 at 8:01 AM

You (usually) do... not sure what in my post lead you to believe that you don't. In fact, the tutorial deals mainly with server-side validation (with exception of Html.EnableClientValidation call)

Generally speaking, client-side vs. server side "validation" is not as black-and-white issue as may seem. See Brad Wilson's post for more details. But I am not sure how this applies to this discussion. Perhaps, your experience with WinForms leads to confusion; not clarification.

 

Coordinator
May 11, 2010 at 6:43 AM
Edited May 11, 2010 at 6:26 PM

Thanks, @vishu. To summarize and add a bit:

  • Client validation is very easy to enable (see p. 63) and should catch most errors on the client rather than in your server-side code
  • Client validaiton and server validation are complimentary - you don't need to pick one or the other. If you have client validation enabled and your user has Javascript turned off, server validation will continue to work as if client validation wasn't enabled
  • ModelState.IsValid will allow you to check if your model is valid without throwing an exception.

@billbris - The exception you're seeing is caused in the StoreDB.SaveChanges() call. At that point, it would be possible to check ModelState.IsValid before saving changes to avoid getting that exception. I didn't do that in the interest of simplicity. If the model is valid but the database update fails for some other reason, we will still need to redisplay the form, and I wanted to reduce multiple code branches that would have to redisplay the form. It is less efficient, but simplifies the code a good amount. I should probably put a note in the tutorial to explain that, though.

May 13, 2010 at 2:05 AM

Hi Jon,

Firstly, thank you for the wonderful tutorial. I've been following it with success and it has proven to be really helpful... up until this particular problem.

Going by your advice, I've added a ModelState.IsValid check in the controller to see if this would fix things, but unfortunately I'm still getting a ConstraintException when I'm trying to edit an Album, and I blank out the Title field. Here's my controller code:

                if (ModelState.IsValid)
                {
                    UpdateModel(album, "Album");
                    storeDB.SaveChanges();

                    return RedirectToAction("Index");
                }

and here's my validation just to be 100%:

        [Required(ErrorMessage = "An Album Title is required")]
        [StringLength(160)]
        public object Title { get; set; }

If i ignore the exception and go back to the form, I can see the field validation appear perfectly fine. I've also tried moving the UpdateModel call outside the if statement to see if that would fix things but I get the same results. Any help would be appreciated!

May 28, 2010 at 3:21 AM
Edited May 29, 2010 at 11:38 PM

Jon, thanks for the insights.

I have added some ModelState code into my [HttpPost] Create album code.  The curious thing was that I still received the same exception. 

I put the code for the Album metadata class, and partial class Album into Models\Album.cs (same directory as StoreMDB.edmx).

I then changed the [DisplayName] for the artist and genre to "ArtistX" and "GenreY" just to see if there was some visual clue that things were working in the form.   Nothing changed.

I then put a breakpoint at the top of my [HttpPost]Create handler, and the exception occured before the breakpoint (so it happens before I can do anything about it).

I then put a breakpoint in the album metadata class.  The breakpoint was never hit.

So, it seems as if the code in Models/Albums.cs is never found or executed.  This would explain why the model code is using the database field requirements (non-nulls).

From what I can tell, my Album.cs file is exactly the same as the published code.  The namespaces match up just fine as well.

Am I missing something that needs re-wiring?

Thanks again!

Bill

 

Update: I've been through your downloaded code to compare my work to yours.  Of course, since I'm copying from your tutorial, there are no differences.  I checked the Models/Album.cs, Controller/StoreManagerController.cs, Views/Shared/EditorTemplates/Album.csx, Views/StoreManager/Create.aspx, and found no real differences.

Is there a build setting, or something like that I'm missing?  I can't figure out why my execution path does not pull in the metadata partial class.

Thanks again.

May 31, 2010 at 2:39 AM

Hi Bill,

Not sure how much this response will come close to answering your question, here is what I think.

I assume that you are receving this error when you run the app in debug mode and have not implemented front end (javascript) validation yet.

Given that, try to change the solution configuration (the dropdown that sits next to the right pointing green arrow for {F5} command) from Debug to Release and press ctrl+shift+B to build it. Now press ctrl+{F5}  to run the app in release mode - without debugging., go to store manager, click on create new  and hit save button. I believe, now you will be able to the error messages.

On the other hand, if you run the app in debug mode, EF layer is validating for the nulls and pauses the app in breakmode. If you press continue to debug by pressing {F5}, then you will see the error messages as usual.

When you implement javascript validation, then you would not see this difference because the validation occurs on the client side before it occurs on the server side.

Hope this helps

Regards, Vasu

May 31, 2010 at 10:41 PM
Edited Jun 2, 2010 at 3:34 PM

Vasuvani,

Thank you for the input.

I stepped through the steps you suggested to switch the configuration from Debug to Release (rebuilding as well).  Each configuration yields the same behavior, the exception is thrown.  This is more of an FYI for you rather than any real info.

You are correct however, when stating that continuing past the exception, the appropriate error messages to appear.

I am going to go through the client-side validation, and what you said makes sense.  I find it odd that the server only side can't get around the exception.  At this point, this is more of a "why did that happen" type problem (given that the client validation works).

So... any idea why the server-only validation gets the exception (in both Debug and Release configurations)?

Curious minds need to know!

B.

 

UPDATE:

I entered the wonderfully small amount of code for client-side validation and everything works as expected.  Everything must be wired properly, so this makes me wonder even more about the server-side validation.  Hmmmm...

Jun 1, 2010 at 7:47 AM

@billbris

yes, server-side validation should work as well. Maybe post the code somewhere - and the curious minds will find the answer together

Jun 2, 2010 at 6:13 AM

@billbris,

Just curious, Did you run it using {ctrl} + {F5} or just {F5}?

Regards, Vasu

 

 

Jun 2, 2010 at 3:40 PM
@vasuvani, Good call... I just pressed F5. Regardless of the configuration (debug and release), the F5 yielded the exception. Using Ctrl+F5 the "issue" disappeared. Problem resolved. Just part of the learning curve (for me anyway), that and read the suggestions more closely. Thanks for all the help! B.
Aug 3, 2010 at 12:48 AM
Edited Aug 3, 2010 at 12:49 AM
jacne wrote:

                    UpdateModel(album, "Album");

UpdateModel is designed to throw when the model isn't valid. If you want a version that doesn't throw, you should use TryUpdateModel. Either way, you should be checking ModelState.IsValid after you update the model.

Alternatively, you could use implicit model binding by way of action parameters. Then you can check ModelState.IsValid right away, since the model binding happens before your action method is even called.

Aug 6, 2010 at 11:32 PM

Hi.  I am fairly new to MVC but from what I have read so far the it sounds like bradwilson is right that model binding is happening before the action method is even called (i believe this occurs when your action method automatically binds the FormCollection into your model when it is envoked.  Correct me if I am wrong.)  I believe this error is a direct result of the fact that the model is based off of a table that requires a value for that field in question.  This is why I am still thinking it's best to base DB calls off of SP's in the database instead of the raw tables (no matter how you do data access: EL, EF, LINQ).

Either way in my opinion the default model binding should not raise an exception in this case.  It should create the model correctly even though the required data is missing and then raise the model's server side validation so it can be sent back to the user.  In the event that the server side validation is turned off (please don't do it!) then the Entity Framework should raise an application error.  It seems like this might be a bug in the Entity Framework -> Model Binding implimentation especially if the use of direct table access using the Entity Framework is being promoted by all of the tutorials from Microsoft.

Aug 10, 2010 at 6:46 PM
I agree that the behavior of MVC + EF here seems less than ideal, but the exceptions which are thrown should be consumed internally by MVC, so you shouldn't really be seeing them. If you place [Required] attributes on the items, then you will get the appropriate required message. Alternative, you could model bind against view models rather then the direct EF objects, and then copy the values from the view model to the EF object (or using the model copier we put into MVC 2 Futures, which makes this a 1-liner).
Oct 12, 2010 at 11:02 PM

Hi everyone,

I'm brand-new to MVC2 after a 3 year departure from .NET, so please bear with me.

Has anyone presented a solution to get server-side validation working with this tutorial? When running the sample code, disabling the client-side validation causes exceptions if you say, leave off an Album title.

TIA,
Brandon

Coordinator
Oct 13, 2010 at 12:32 AM

Brandon - Welcome back! :-)

The short answer there is that server side validation is working as expected. You will only see it when debugging your code in Visual Studio; the code won't throw exceptions when running in release mode.

Longer answer: Those are expected exceptions, because that's how Entity Framework passes the constraint information back to the MVC framework. Since the EF model code is part of your project and Visual Studio's default behavior is to break on exceptions in code, you get an unnerving dialog that looks like an error, but it's not. I've clarified this in the new release of the tutorial, which was published last friday.