Introduction to ASP.NET Core part 12: data annotation of view models

In the previous post we went through how to insert a new Book entity using a form. We used a view model for this purpose. Two different Create action methods take care of the insertion process in the Books controller. One accepts HTTP GET requests and only presents an empty form with the various book insertion view model properties. The corresponding view has a form that’s built using a Html helper which takes care of setting up the form with the correct naming conventions so that the view model properties can be correctly populated. We also extended our rudimentary layered architecture to simulate that the Book entity is saved in a data store. Finally we also mentioned the usage of the anti-forgery token attribute which guards against a cross site request forgery attack.

In this post we’ll look at how attributes on our attributes can help us refine our view models and how they are rendered in the view.

There are various attribute types we can attach to these properties that affect the behaviour of the view model in various ways. These attributes reside in the System.ComponentModel.DataAnnotations namespace. As it stands now our book insertion form is very basic. Apart from the lack of styling, which is of no interest to us at this phase, there’s no data validation, there are no restrictions imposed on the values that the user can enter and the NumberOfPages label is not too fancy.

Controlling the display

The Display attribute can define the label for the property in the view:

The Display attribute has a number of properties with Name and Prompt probably the one used most often. Name defines the label of the property and Prompt will set a placeholder that disappears as soon as the user starts typing in the text box:

Form validation

Data validation has several attributes:

Required: the user must enter a value

Range: the user must enter a numeric value within an interval

MinLength and MaxLength: the string must be either be at least or at most as long as the defined lengths

These annotations will provide a basic level of validation of the form data in the backend. It’s important to keep in mind that these attributes won’t magically create client side validation scripts. Also, complex validation will still be part of your domain logic. E.g. at my company the customer can perform load testing of their websites. The rules around who and when they can start a load test are quite complex and intricate which cannot possibly be expressed with data annotations. Data annotations can validate simple things like the passwords match when a new user wants to sign up with our service. This level of validation is perfect to reject the most obvious mistakes. Don’t try to put all your domain validation rules in data annotations, they should be part of the domain classes.

Before we apply some of these attributes we need to extend the book insertion form. We should enter placeholders to show the validation exception messages to our users. There’s a Html helper called ValidationMessageFor that we can apply here:

We’re not done yet. If you test entering an invalid book now all you’ll see is that the page is redirected to the book index page and the book with the invalid data is still inserted into the data store. Data annotation in itself will not prevent the controller from executing its POST Create action method. We’ll need to check the model state first. If there’s a validation error then the model state will know about it but the exceptions it has in its list won’t be applied automatically in our controller code. The POST Create method will need to be extended as follows:

If the model state is valid then we enter a book. Otherwise the same view will be presented. MVC will figure out that we mean the current book insertion view presented by GET Create. That’s how the user will see the data validation exceptions:

As soon as you insert valid data the book will be added to the in-memory data store.

There’s also another way to present the data validation messages. The validation summary helper method can be placed within the form:

In order to fully reap the benefits of the DataType attribute we need to make a little change in Create.cshtml. The currently used TextBoxFor gives no freedom to MVC to decide what kind of input type it should render. We tell it to show a text box which will be an input of type text:

Now try to enter a book with a title that doesn’t look like a URL. I’m testing in Chrome and it gives me the following validation message:

The DataType enumeration also has a Text value which will set the type to “text” like we had before. Feel free to test the other data types too. The password type is widely used in signup and login forms. Its effect is that the user won’t see the characters in the text box but only some black dots. That’s of course the well know behaviour of password text boxes.

The post has demonstrated an additional advantage with using view models in our controllers. The attributes can be placed in our view models instead of putting them directly on our domain classes which would be very wrong. Domain models should stay as clean as possible and not be littered with view related attributes that are solely in place for the controller and its matching view.