How data annotations for ASP.NET MVC validation work

Data validation is one of the most important aspects of developing applications for the web. However, validation is also something that can get messy pretty quickly, as developers often stick validation code anywhere and everywhere. However, if you keep a clear separation of concerns by using the MVC pattern and couple that with data annotations in the model, then your validation code becomes much simpler to write, maintain, and test. The added bonus: simple code that’s well written & tested is code that’s just going to work.

What are data annotations?

Data annotations are attribute classes that live in the System.ComponentModel.DataAnnotations namespace that you can use to apply to (decorate) classes or properties to enforce pre-defined validation rules. These annotations are available across various Visual Studio 2010 project types including ASP.NET MVC, Web Forms, ASP.NET Apps & Web Sites, Dynamic Data & even non ASP.NET projects like Silverlight & WPF. The same holds true for the data models; you can use these annotations with POCOs (plain old CLR objects), EF models, Linq2SQL models, etc… The bottom line is that you can use data annotations anywhere.

There are several out of the box data annotations to choose from:

  • Required
  • Regular Expression
  • Range
  • ZipCode
  • DisplayName
  • DisplayFormat
  • Scaffold
  • DataType
  • StringLength
  • A few more…

Although having this set of built in validations is great, it is a limited set. Since every business has lots of rules that don’t neatly fit into this pre-defined set of attributes, what do you do when you need to use your own logic? It’s quite easy, really – just derive from any of the inheritable (non sealed) attribute classes in the System.ComponentModel.DataAnnotations namespace, then code your business rules in the derived class methods.

Between the built-in validators and your own custom validation code, you’re all set with maintainable and clean validation. However, server side data sanitization & validation is also equally important. An upcoming blog post will cover creating custom & server side validation. In the meantime, Brad Wilson from the ASP.NET team has some great blog posts on unobtrusive validation along with a a great series of posts on MVC.

And it all starts in the data model…

Applying data annotations to the model.

Suppose a data entry clerk needs to frequently update baked products with baked and expiration dates, amounts, prices, etc… To accomplish this in code, start with a basic POCO class to represent a product and will serve as the data model to meet the needs of this scenario. You’ll need to do more than just create the class and its members though, and that’s where annotations come into play.

Data annotations serve as a way to enforce these common validation scenarios without having to write much code, and more importantly, without writing repetitive code. Annotations live in one place – the model, rather than a code behind, web page, controller, or view. This helps prevent data validation code from turning into Spaghetti# code[1] scattered about your project.

Take a look at the following Product class, complete with a few basic data annotations:

public class Product 
{
public int Id { get; set; }

[DisplayName("Delicious Treat")]
[Required(ErrorMessage = "The product name field is required.")]
public string Name { get; set; }

[Required(ErrorMessage = "The product description field is required.")]
public string Description { get; set; }

[DisplayName("Sale Price")]
[Required(ErrorMessage = "The Sale Price field is required.")]
public decimal Price { get; set; }

[DisplayName("Freshly Baked on")]
[Required(ErrorMessage = "The Freshly Baked On field is required.")]
public DateTime CreationDate { get; set; }

[DisplayName("Don't Sell After")]
[Required(ErrorMessage = "The Expiration Date field is required.")]
public DateTime ExpirationDate { get; set; }

[DisplayName("Qty Available")]
[Required(ErrorMessage = "The Qty Available field is required.")]
[Range(0,120)]
public int QtyOnHand { get; set; }

[DisplayName("Product Image")]
public string ImageName { get; set; }
}

The above properties are labeled with attributes that clearly state their intentions. The bulk of the validation is simply the Required attribute for this sample, along with a Range attribute on the QtyOnHand property. Some attributes such as the DisplayName attribute don’t necessarily validate but do affect the rendered output. As the above sample demonstrates, any combination of necessary attributes can decorate properties. Notice that all the model and validation code so far live in the same place, and will continue to do so when you create custom attributes and validators; meaning that you’ll continue to have a nice, clean separation of concerns in your app.

Once you’re done applying attributes to the model, you can move on to the controllers and views.

Where do views & controllers fit in?

Views:

Once you’ve setup validation on the model using data annotations, they’re automatically consumed by Html Helpers in views so the helpers can render the proper HTML output. For client side validation to work, you will need to ensure that these two <SCRIPT> tag references are in your view:

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<
script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script
>

Note: If you’re using ASP.NET MVC 3 project templates, these script tags will be added in views automatically for you by selecting the “reference client script libraries” option in the Add View dialog.

The following code from an edit or insert view will make sure the DisplayName, Required, and Range attributes work at run time.

<div class="editor-field">
@Html.EditorFor(model => model.QtyOnHand)
@Html.ValidationMessageFor(model => model.QtyOnHand)
</div
>

Other helper methods such as @Html.TextBox, @Html.TextBoxFor, @Html.Editor, etc… render nearly identical yet very slim output, explained more later in this post. Views play an integral part in validation, yet as the developer you often won’t need to do much in the view other than use the built-in Html Helpers.

Controllers:

Controllers serve as a gateway to accept HTTP POST requests to process incoming data then to match that data with its model and view for rendering. Controllers shouldn’t be a dumping ground for validation code, server side validation code should be called from controller action methods. While you can put validation code directly into the action methods themselves; the practice tends to lead to code that’s less than optimal to maintain, since you’ve started scattering the code about the project. Rather than having code in multiple locations, think about moving server side validation code to a single location near or in the model, or as its own library project.

How validation works.

Both client and server side validation work because of a few conventions in your project that match up data annotations, Html Helpers, rendered output. Html Helpers in views render HTML elements containing attributes that start with the pattern data-val-*. The data-val-* attributes contain error messages, regular expressions, ranges, and other validation information that originates in data annotations.

That means that this decorated code in the model…

[DisplayName("Qty Available")]
[Required(ErrorMessage = "The Qty Available field is required.")]
[Range(0,120)]
public int QtyOnHand { get; set; }

…combined with this code in the view…

<div class="editor-field">
@Html.EditorFor(model => model.QtyOnHand)
@Html.ValidationMessageFor(model => model.QtyOnHand)
</div
>

…turns into this HTML at runtime…

<div class="editor-field">
<input class="text-box single-line"
data-val="true" data-val-number="The field Qty Available must be a number."
data-val-range="The field Qty Available must be between 0 and 120."
data-val-range-max="120" data-val-range-min="0"
data-val-required="The Qty Available field is required."
id="QtyOnHand" name="QtyOnHand" type="text" value="12" />
<span class="field-validation-valid" data-valmsg-for="QtyOnHand" data-valmsg-replace="true"></span
>
</
div
>

The tie-in between the data model annotations and the data-val-* attributes should be clear after reading the above code, but it’s where the client side validation ties in might not be so obvious. Open the \Scripts\jquery.validate.unobtrusive.js file and search for “data-val”. Right away you’ll see that the JavaScript uses the data-val-*, input-* and field-* CSS classes to display/hide validation messages on the client. Although you shouldn’t modify or need to maintain the built-in .js files; it’s worth investigating them to see how things work together in ASP.NET MVC.

function onError(error, inputElement) {  // 'this' is the form element
var container = $(this).find("[data-valmsg-for='" + inputElement[0].name + "']"),
replace = $.parseJSON(container.attr("data-valmsg-replace")) !== false;

container.removeClass("field-validation-valid").addClass("field-validation-error");
error.data("unobtrusiveContainer", container);

if (replace) {
container.empty();
error.removeClass("input-validation-error").appendTo(container);
}
else {
error.hide();
}
}

Having the onError function tied in by only HTML attributes keeps client side validation unobtrusive, or in other words, out of the way of your view code. Annotations combined with unobtrusive validation make both the view and the output are very clean and maintainable. The final result in the browser is fully capable client side validation that falls back to server side validation for browsers without JavaScript enabled. Either way, the same validation happens and the user sees the same error.

image_thumb2

Summary & Code

Data annotations give you a nice way to keep validation and business logic close to the model, and easier to maintain, test and debug. Additionally, cleaner, simpler output leads to faster download speeds and easier integration with client side script libraries like jQuery for UI manipulation.

Although this post focuses on data annotations and client side script, don’t forget that server side validation is very important.

Download the code.

Notes: [1] Spaghetti# is not a real language.

Integrating ASP.NET Web Forms and ASP.NET MVC

With ASP.NET MVC being a mature platform, many Web Forms developers are wondering whether or not they should move to MVC or are thinking about integrating MVC into their existing Web Forms projects. Web Forms has caught up to MVC in many respects. For example, Web Forms V4+ supports routing, and the controls render less HTML than ever before. ViewState went on a diet and is considerably smaller. In the past some have turned to MVC to address these concerns, but no longer is this the case.

If you’re interested in more content about migrating or integrating Web Forms and MVC, see my WintellectNOW video, “Migrating from Web Forms to MVC”. It covers the many concerns developers face when making the decision about whether or not to migrate to a new technology (and whether or not you should at all). In the meantime, below are some practical tips and techniques required when integrating the two technologies.

MVC Integration: Setup, References, and Configuration

You can install the ASP.NET MVC 4 NuGet package that includes all the resources that you need to use MVC such as references, web.config changes, etc… Once you’ve installed the NuGet package there is little you need to do to use MVC except create folders named “Models”, “Views”, and “Controllers” (assuming you are keeping all these in the same project).

image_thumb3

The NuGet Package adds in several changes to the web.config file as well as references to the project that are worth examining. The web.config sample below shows these references:

Location: <configuration><appSettings>

Configure the ASP.NET Web Pages rendering engine settings in the <appSettings> element.

<appSettings>
  <add key=webpages:Version value=2.0.0.0 />
  <add key=webpages:Enabled value=false />
  <add key=PreserveLoginUrl value=true />
  <add key=ClientValidationEnabled value=true />
  <add key=UnobtrusiveJavaScriptEnabled value=true />
</appSettings>

Location: <configuration><system.web>

This code adds various names
paces to the project’s compilation as well as parser filter and base types, which are settings that determine how views render. The namespace references below are the automatic imports to namespaces for use in views only. You must continue use the using keyword in C# code. The general namespace inclusion is in the previous example.

<pages pageParserFilterType=System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc,
       Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35
       pageBaseType=System.Web.Mvc.ViewPage, System.Web.Mvc,
       Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35
       userControlBaseType=System.Web.Mvc.ViewUserControl, System.Web.Mvc,
       Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35>
  <namespaces>
    <add namespace=System.Web.Optimization />
    <add namespace=System.Web.Helpers />
    <add namespace=System.Web.Mvc />
    <add namespace=System.Web.Mvc.Ajax />
    <add namespace=System.Web.Mvc.Html />
    <add namespace=System.Web.Optimization />
    <add namespace=System.Web.Routing />
    <add namespace=System.Web.WebPages />
    <add namespace=System.Web.WebPages.Html />
  </namespaces>
  <controls>
    <add assembly=Microsoft.AspNet.Web.Optimization.WebForms
     namespace=Microsoft.AspNet.Web.Optimization.WebForms
     tagPrefix=webopt />
  </controls>
</pages>

Location: <configuration><system.webServer><handlers>

These are the ASP.NET runtime HTTP handlers. They are what determines which .dlls service which type of requests at a low level. As demonstrated, these handlers are looking to interact with HTTP via verbs.

<handlers>
  <remove name=ExtensionlessUrlHandler-ISAPI-4.0_32bit />
  <remove name=ExtensionlessUrlHandler-ISAPI-4.0_64bit />
  <remove name=ExtensionlessUrlHandler-Integrated-4.0 />
  <add name=ExtensionlessUrlHandler-ISAPI-4.0_32bit path=*.
       verb=GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS modules=IsapiModule
       scriptProcessor=%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll
       preCondition=classicMode,runtimeVersionv4.0,bitness32 responseBufferLimit=0 />
  <add name=ExtensionlessUrlHandler-ISAPI-4.0_64bit path=*.
       verb=GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS modules=IsapiModule
       scriptProcessor=%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll
       preCondition=classicMode,runtimeVersionv4.0,bitness64 responseBufferLimit=0 />
  <add name=ExtensionlessUrlHandler-Integrated-4.0 path=*.
       verb=GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS type=System.Web.Handlers.TransferRequestHandler
       preCondition=integratedMode,runtimeVersionv4.0 />
</handlers>

Location: <configuration><runtime><assemblyBinding>

Of course if you’re dealing with differing versions of .NET for any type project, you must change assembly bindings. Assembly bindings declare which versions of which libraries to reference in the project. VS will compile against the new redirected binding than the default when you use the settings below:

<dependentAssembly>
  <assemblyIdentity name=System.Web.Helpers publicKeyToken=31bf3856ad364e35 />
  <bindingRedirect oldVersion=1.0.0.0-2.0.0.0 newVersion=2.0.0.0 />
</dependentAssembly>
<dependentAssembly>
  <assemblyIdentity name=System.Web.Mvc publicKeyToken=31bf3856ad364e35 />
  <bindingRedirect oldVersion=1.0.0.0-4.0.0.0 newVersion=4.0.0.0 />
</dependentAssembly>
<dependentAssembly>
  <assemblyIdentity name=System.Web.WebPages publicKeyToken=31bf3856ad364e35 />
  <bindingRedirect oldVersion=1.0.0.0-2.0.0.0 newVersion=2.0.0.0 />
</dependentAssembly>

After configuring the project to incorporate MVC, you’ll need to setup routing.

Controllers and Routing

Routing is an essential component of MVC development, and MVC cannot work without it. In Web Forms V4 and later, routing is baked right in. What you’ll need to do to ensure the proper routes are set is to create a static RouteConfig class, preferably in a file (e.g., RouteConfig.cs) inside the App_Start special folder. After that, add in the code below to assign a generic route.

routes.MapRoute(
    name: “Default”,
    url: “{controller}/{action}/{id}”,
    defaults: new { controller = “Home”, action = “Index”, id = UrlParameter.Optional }
);

Of course, the RouteConfig class won’t instantiate itself. That means you must call its RegisterRoutes function (also static) from the Global.asax.cs file like so:

void Application_Start(object sender, EventArgs e)
{
    RouteConfig.RegisterRoutes(RouteTable.Routes);
}

The last thing to do is use MVC conventions to create the appropriate folders for Models, Views, and Controllers, if you haven’t already. Otherwise, structure your project as you would for use with the MVC pattern, perhaps moving models to their own class library or service layer.

Considerations when integrating Web Forms and MVC

DataSets/DataReaders & Models:

A lot of data is accessed by Web Forms via declarative tags like the <asp:SqlDataSource>, as well as volumes of DataSets and DataReaders in the code behind files. You’ll need to find a strategy for converting these to models, i.e., classes. Some Web Forms projects contain POCOs for models and those often port over quickly. By convention (assuming a single project), model files live in the <ProjectRoot>\Models folder and model classes in the <Project>.Models namespace.

ASPX & Views:

ASP.NET MVC ships with two view engines: Web Forms (ASPX) and Razor. The ASPX view engine continues on using the classic <%# %> syntax for code delimiters, while Razor uses only the @ symbol (or @{}) as a way to denote the start of of code. Razor uses a new parsing engine first introduced in ASP.NET Web Pages that produces clean and concise HTML output. However, if you have a team strong in Web Forms they may want to continue using Web Forms syntax. The difference worth mentioning is that the ASPX view engine uses the traditional Web Forms rendering under the hood so technically it builds a control tree and renders it. You just can’t access the elements in MVC views like you can in a Web Forms page by referring to a control as an object in the code behind. This makes Razor much faster as there is no tree to render. In  views, code renders inline, or in placeholders, rather than from scripted code in a code behind. This is a drastic enough difference that you’ll probably need to completely rewrite many Web Forms as MVC views, as opposed to simply reusing pages in MVC. Of course, redoing all pages as views all at once is often not an option because of time and budget, but you can move them one by one.

Any would be controls in MVC become Html helpers. Html Helpers are the cornerstone of ASP.NET MVC views. Like controls, the Html Helpers render chunks of browser friendly output, but helpers emit the least amount of code as possible and in the least obtrusive way. Helpers also accept and render data from the model. This means if you have 3rd party controls, you might want to migrate these pages last or weigh the option of leaving them as Web Forms. This, of course, depends upon the cost-benefit of the developer’s time to build a control vs. buying a new version of one, if one is available. Most popular 3rd party vendors are up to date.

Code Behind Files & Controllers:

It is not uncommon to see a Web Forms code behind file with 5,000+ lines of code. That’s a lot of code for one single Page object. It also makes the page object a Swiss army knife of sorts as Page objects deal with building the UI and doing all sorts of other things. Many developers add CRUD operations directly in the code behind. This page of all trades ends up doing all the chores and this means more complex code and code that is tightly coupled.

MVC leans toward a concept called Separation of Concerns in which each part of the MVC pattern maps to a concern. M for models or data and CRUD operations. V for views or UI. C for controllers or operational code. All the layers are nicely separated. If your Web Forms code is getting a bit unruly or spaghetti-like then MVC could address and fix some of your maintenance issues by breaking out components into manageable units, or concerns.

Controllers of course make the calls to Web services to retrieve JSON or XML data or directly to the database, a repository, or any number of options. Controllers also do the work of determining how HTTP requests are handled. This responsibility falls to the code behind in Web Forms.

ASP.NET Core:

Since it’s all ASP.NET, you get to share core features such as sessions, caching, globalization, Forms authentication between Web Forms and MVC.

Summary

Fortunately assets like images, CSS, and custom JavaScript are all reusable between Web Forms and MVC projects. Just keep the ID and NAME attributes the same (which you want anyway for consistency). As you can see, both technologies are ASP.NET so they can live harmoniously and even compliment each other.

As you can see, with just a few modifications to a Web Forms project and you can start adding MVC components. You can run both Web Forms and MVC or work toward doing a complete migration.