FluentValidation C4mvc presentation

This Wednesday I’m going to be giving a live presentation on using FluentValidation for the c4mvc virtual usergroup.

The session will be live from 18:00 GMT (12:00 CST) and will last about 40 minutes. I’ll be covering the basics of using FluentValidation as well as how you can integrate it with ASP.NET MVC applications.

FluentValidation 1.2 Beta 2 and MVC2 RC2

FluentValidation 1.2 beta 2 is now available to download. This release focuses mainly on integration with ASP.NET MVC 2.

Since my post on Limitations of MVC2’s ModelValidatorProviders ASP.NET MVC2 RC2 has been released which addresses some of the issues that I raised in the post.

Firstly, validation in MVC2 RC2 now performs model-validation rather than input validation. This is much more in line with how FluentValidation works which makes the FluentValidation integration much more consistent with the built-in validation.

Secondly, it is now possible to disable the DataAnnotationsModelValidatorProvider’s “greedy” required rule. Out of the box, the DataAnnotationsModelValidatorProvider will *always* validate non-nullable value types, irrespective of whether the property is decorated with a [Required] attribute. This meant that you had to un-register the DataAnnotations provider if you didn’t want to get duplicate errors. It is now possible to turn this off by calling DataAnnotationsModelValidatorProvider.AddImplicitRequiredAttributeForValueTypes = false; in your application startup routine.

Finally, I added the ability to be able to execute some of FluentValidation’s Property Validators without needing to validate the entire object. This means it is now possible to stop the default “A value was required” message from being added to ModelState.

Enabling the FluentValidationModelValidatorProvider

After downloading the 1.2 beta 2 release you’ll need to add a reference to FluentValidation.dll and FluentValidation.Mvc.dll

To enable the FluentValidationModelValidatorProvider you will need to add it to the ModelValidatorProviders collection in your application startup routine:

protected void Application_Start() {
	RegisterRoutes(RouteTable.Routes);
 
	DataAnnotationsModelValidatorProvider
		.AddImplicitRequiredAttributeForValueTypes = false;
 
	ModelValidatorProviders.Providers.Add(
		new FluentValidationModelValidatorProvider(new AttributedValidatorFactory()));
}

Note that you need to pass an IValidatorFactory to the FluentValidationModelValidatorProvider so that it knows how to create your validator instances. I’m using the AttributedValidatorFactory in this example, but in a real application you would probably use an implementation that wraps an IoC container instead.

Next, you can create your model object and associated validator:

[Validator(typeof(PersonValidator))]
public class Person {
	public int Id { get; set; }
	public string Name { get; set; }
	public string Email { get; set; }
	public int Age { get; set; }
}
 
public class PersonValidator : AbstractValidator<Person> {
	public PersonValidator() {
		RuleFor(x => x.Id).NotNull();
		RuleFor(x => x.Name).Length(0, 10);
		RuleFor(x => x.Email).EmailAddress();
		RuleFor(x => x.Age).InclusiveBetween(18, 60);
	}
}

Finally, you can create the controller and associated view:

public class PeopleController : Controller {
	public ActionResult Create() {
		return View();
	}
 
	[AcceptVerbs(HttpVerbs.Post)]
	public ActionResult Create(Person person) {
 
		if(! ModelState.IsValid) {
			return View("Create", person);
		}
 
		TempData["notice"] = "Person successfully created";
		return RedirectToAction("Index");
 
	}
}
 
<%= Html.ValidationSummary() %>
 
<% using (Html.BeginForm()) { %>
	Id: <%= Html.TextBoxFor(x => x.Id) %><%= Html.ValidationMessageFor(x => x.Id) %>
	<br />
	Name: <%= Html.TextBoxFor(x => x.Name) %><%= Html.ValidationMessageFor(x => x.Name) %>			
	<br />
	Email: <%=Html.TextBoxFor(x => x.Email) %><%= Html.ValidationMessageFor(x => x.Email) %>
	<br />
	Age: <%= Html.TextBoxFor(x => x.Age) %><%= Html.ValidationMessageFor(x => x.Age) %>
 
	<br /><br />
 
	<input type="submit" value="submit" />
<% } %>

Now when you post the form MVC’s DefaultModelBinder will validate the Person object using the FluentValidationModelValidatorProvider.

Clientside Validation

This release also has preliminary support for clientside validation. This is enabled by calling <% Html.EnableClientValidation(); %> in your views. At the moment, only the NotNull, NotEmpty, RegularExpression and Length validators are supported.

Note that if you try to use the clientside validation support with the default error messages, you will see error messages like this:

'{PropertyName}' must not be empty.

This is because FluentValidation uses templated error messages but the MVC Framework’s clientside validation runner does not know how to correctly format these. For now, the only workaround is to explicitly specify the error message:

RuleFor(x => x.Surname).NotNull().WithMessage("'Surname' must not be empty");

For FluentValidation 1.3 I will consider providing a customised version of MVC’s MicrosoftMvcValidation.js that knows how to properly format FluentValidation error templates.

Metadata Support

Out of the box, MVC2 allows you to use the DataAnnotations attributes to provide metadata for editor/display templates. This release of FluentValidation allows you to use extra extension methods on your validator classes to define metadata using method chaining.

Firstly, you will need to replace the DataAnnotationsModelMetadataProvider with the new FluentValidationModelMetadataProvider in your Application_Start:

ModelMetadataProviders.Current = new FluentValidationModelMetadataProvider(
	new AttributedValidatorFactory());

Now you can start using the metadata extension in your validator classes (you will need to import the FluentValidation.Mvc.MetadataExtensions namespace):

public CustomerValidator() {
   RuleFor(x => x.Surname)
      .NotNull() //regular validator
      .UIHint("Surname"); //MVC Metadata
}

The FluentValidationModelMetadataProvider currently supports the following metadata-related extension methods which correspond to the DataAnnotations attributes with the same name:

  • HiddenInput
  • UIHint
  • Scaffold
  • DataType
  • DisplayName
  • DisplayFormat
  • ReadOnly

Edit: 8th FebNote that there is a bug in the current beta build where MetaData won’t work in some circumstances. This is already fixed for the next beta, so you may want to use the latest binaries from the build server.

As usual, binaries are on CodePlex and the source is on GitHub.

Linq to Sql and ASP.NET MVC – DataLoadOptions per Request

This is the third in a series of posts on using ASP.NET MVC with Linq to Sql:

Code for this series is available here.

One common problem when using an ORM is the issue of “select n+1″. For example, take the following Linq to Sql query:

var context = new BlogDataContext();
var posts = context.Posts;
 
foreach(var post in posts) {
   foreach(var comment in post.Comments) {
     var commenter = comment.Commenter.Name;
     Console.WriteLine("User {0} commented on post {1}", commenter, post.Title);
   }
}

This might look fine on the surface, but by default Linq to Sql will lazily-load all associations. This means that for each post loaded from the database Linq to Sql will issue a query to load the comments, and for each comment Linq to Sql will issue another query to load the commenter. So if you have 10 posts, and each post has 5 comments then you’ll end up making 61 queries to the database (1 for the posts, 1 for each post to get the comments, and 1 for each comment to get the user)

This can be mitigated by using DataLoadOptions to eagerly load all of the data in a single query:

var context = new BlogDataContext();
 
var options = new DataLoadOptions();
options.LoadWith<Post>(p => p.Comments);
options.LoadWith<Comment>(c => c.Commenter);
context.LoadOptions = options;
 
var posts = context.Posts;
foreach(var post in posts) {
 //...
}

The Problem

However, once a query has been executed, the LoadOptions property of a DataContext is frozen – you cannot then specify any additional eager loading paths.*

If you’re using a DataContext per request as per my previous post then this can be a problem. For example, imagine you have the following controller action:

public class PostsController : Controller {
  private BlogDataContext context;
 
  public PostsController(BlogDataContext context) {
     this.context = context; //injected via our IoC container
  }
 
  public ActionResult Show(int id) {
    var options = new DataLoadOptions();
    options.LoadWith<Post>(p => p.Comments);
    options.LoadWith<Comment>(c => c.Commenter);
 
    context.LoadOptions = options;
 
    var post = context.Posts.Single(p => p.Id == id);
    return View(post);
  }
}

…then this will work as expected. But now imagine your action is decorated by an ActionFilter:

[LoadCurrentUser]
public ActionResult Show(int id) { 
  //...
}

…and let’s assume that our LoadCurrentUser filter attempts to load the details of the current user from the database and store them in ViewData:

public class LoadCurrentUserAttribute : ActionFilterAttribute {
	public override void OnActionExecuting(ActionExecutingContext filterContext) {
		var context = ObjectFactory.GetInstance<BlogDataContext>();
		var identity = filterContext.HttpContext.User.Identity;
 
		if (identity.IsAuthenticated) {
			var currentUser = context.Users.Single(x => x.UserName == identity.Name);
			filterContext.Controller.ViewData["CurrentUser"] = currentUser;
		}
	}
}

Now when the controller is executed we will get the following error:


Setting load options is not allowed after results have been returned from a query.

This happens because we’re trying to set the LoadOptions for the Post after the LoadCurrentUser filter already executed a query.

The Solution

The approach I use to work around this problem is to use a custom AuthorizationFilter in conjunction with an eager loading specification.

First, we define an interface for eager loading specifications:

public interface IEagerLoadingSpecification {
  void Build(DataLoadOptions options);
}

Next, we can write some classes that implement IEagerLoadingSpecification that define particular specifications for eager loading. Using the example above, we have PostWithComments and CommentWithCommenter:

public class PostWithComments : IEagerLoadingSpecification {
  public void Build(DataLoadOptions options) {
    options.LoadWith<Post>(p => p.Comments);
  }
}
 
public class CommentWithCommenter : IEagerLoadingSpecification  {
  public void Build(DataLoadOptions options) {
     options.LoadWith<Comment>(c => c.Commenter);
  }
}

Next, we write an EagerlyLoadAttribute which implements IAuthorizationFilter and takes an array of Types in its constructor:

public class EagerlyLoadAttribute : FilterAttribute, IAuthorizationFilter {
  private Type[] types;
 
  public EagerlyLoadAttribute(params Type[] types) {
    this.types = types;
  }
 
  public void OnAuthorization(AuthorizationContext filterContext) {
    var loadOptions = new DataLoadOptions();
    var context = ObjectFactory.GetInstance<BlogDataContext>();
 
    foreach(var type in types) {
       if(! typeof(IEagerLoadingSpecification).IsAssignableFrom(type)) {
          throw new InvalidOperationException(string.Format("Type {0} does not implement IEagerLoadingSpecification", type));
       }
 
      var spec = (IEagerLoadingSpecification)Activator.CreateInstance(type);
      spec.Build(loadOptions);
    }
 
   context.LoadOptions = loadOptions;
  }
}

So what does this filter do? Firstly, because it implements IAuthorizationFilter this means it will be invoked before any action filters that decorate your controller action. When OnAuthorization is invoked, it loops over each of the Types that we’ve passed to its constructor, instantiates them and casts them to IEagerLoadingSpecification. Next, a single DataLoadOptions instance is passed to each of the specifications in turn so they can build up the required eager loading paths.

The end result is that you can now define your actions like this:

  [EagerlyLoad(typeof(PostWithComments), typeof(CommentWithCommenter))]
  [LoadCurrentUser]
  public ActionResult Show(int id) {
    var post = context.Posts.Single(p => p.Id == id);
    return View(post);
  }

Because the EagerlyLoadAttribute is an IAuthorizationFilter, it will be invoked before anything else meaning that by the time both our LoadCurrentUser filter and the Show action are invoked the DataLoadOptions have already been set on our DataContext.

* Note that other ORMs like NHibernate, LLBLGen and the Entity Framework don’t have this problem because they allow eager loading paths to be specified at the query level.

SQL Search 1.0 Released

If you haven’t tried out Red Gate’s SQL Search yet, be sure to grab the v1.0 which was released today. I’ve been running the beta for the last couple of weeks and have been finding it quite useful when navigating around large SQL Server databases.

If you’ve used JetBrains’ ReSharper before, you can think of SQL Search like ReSharper’s Go to Type functionality, but for SQL Server objects rather than classes. When you hit Ctrl+Alt+D you can begin typing the first few characters of an object name and it will list all the matches for you:

Hitting ‘enter’ will take you to the selected object in the Object Explorer.

As a heavy keyboard user, I find this much more usable than navigating the Object Explorer with a mouse (especially for larger databases).

The only complaint I’d have is that the filtering options aren’t fine-grained enough. At the moment, you can filter the list on one of the following options:

…but I’d find it more useful if you could pick multiple criteria (for example, I’d like to search the names of Tables, Views and Procedures, but not include Constraints and Triggers). Hopefully this will be added for the next version.

Linq to Sql and ASP.NET MVC – AutoCommit and the RoutePreParser

This is the second in a series of posts on using ASP.NET MVC with Linq to Sql:

Code for this series is available here.

In my previoust post, I demonstrated how you could scope a Linq to Sql DataContext to a single HTTP Request by using StructureMap to manage the lifetime of the DataContext instance. This often works well, but has a couple of gotchas. Let’s take a look at an example:

public class PostController : Controller {
	private readonly BlogDataContext context;
 
	public PostController(BlogDataContext context) {
		this.context = context;
	}
 
	public ActionResult Create() {
		return View();
	}
 
	[AcceptVerbs(HttpVerbs.Post), AutoCommit]
	public ActionResult Create(Post post) {
		context.Posts.InsertOnSubmit(post);
		return RedirectToAction("Show", new{ id = post.Id });
	}
 
	public ActionResult Show(int id) {
		var post = context.Posts.SingleOrDefault(x => x.Id == id);
		if (post == null) {
			throw new HttpException(404, "The post could not be found.");
		}
 
		return View(post);
	}
}

In this example, we have a Post controller for our fictional Blog database. The Create action renders a view where the user can create a new Post and then sends an HTTP POST to the other overload to the Create action (decorated with AcceptVerbs(HttpVerbs.Post)).

This action is decorated with the AutoCommit attribute from my previous post so that SubmitChanges will automatically be called on our DataContext. We then redirect to the “Show” action passing the Id of the newly created post in the route data.

The Show action loads the Post from the database with the corresponding Id and displays it to the user. If a post with the specified Id could not be found, it will throw a 404 exception.

There is a bug here that may not be immediately obvious. Assume that the Post’s Id property is generated by an auto-incrementing Identity field in the database. If you were to run this application and create a new Post, the Show action will always throw 404 even though the Post has been successfully created.

This happens because of when SubmitChanges is called. Our AutoCommit filter is invoked after the action has finished executing this means that at the time we call RedirectToAction(“Show”, new{ id = post.Id }) the new post has not yet been saved, so its Id will be 0.

Linq to Sql will not update the Id property until after SubmitChanges is called, which is too late in the process. The user will end up being redirected to Post/Show/0 instead of using the Id of the newly created post.

The simple workaround is to call SubmitChanges directly inside the action:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Post post) {
	context.Posts.InsertOnSubmit(post);
 
	if (ModelState.IsValid) {
		using (var transaction = new TransactionScope()) {
			context.SubmitChanges();
			transaction.Complete();
		}
	}
 
	return RedirectToAction("Show", new { id = post.Id });
}

…but this then makes our AutoCommit attribute useless and also makes the controller action significantly more complex.

However, there is another approach we could use which still allows us to use the AutoCommit attribute. To understand this, we first need to look at the order in which things are happening when our action is invoked:

  1. The IoC container creates our DataContext
  2. The controller is instantiated and the DataContext is passed to its constructor
  3. The Create action is invoked with a Post instance created by MVC’s DefaultModelBinder
  4. The post is attached to the DataContext by calling InsertOnSubmit
  5. A RedirectToRouteResult is created by calling “RedirectToAction” with a dictionary of route values
  6. The AutoCommit attribute calls SubmitChanges to our DataContext
  7. The Post is written to the database
  8. ExecuteResult on RedirectToRouteResult is invoked
  9. The Redirect URL is generated by the RouteCollection
  10. The user’s browser is redirected to this URL

Instead of passing the Post Id (which will be 0) to RedirectToAction, we could pass the entire post instance. After the post has been saved we can then *replace* the post instance in the RouteValueDictionary with the post’s Id before the URL is generated. This can be done by intercepting the RouteValueDictionary just before the URL is generated.

Introducing the RoutePreParser

The first thing we need is a way to identify how an object (in this case, our Post) should be converted to a route value. To do this, we can create an interface, IUrlRoutable:

public interface IUrlRoutable {
	object GetRouteParameter();
}

…and we can implement this interface in our Post class:

public partial class Post : IUrlRoutable {
	public object GetRouteParameter() {
		return Id;
	}
}

Next we can create a “fake” route. This route never generates a URL or handles a request – it is merely used to intercept the RouteValueDictionary before a URL is generated:

public class RoutePreParser : RouteBase {
	public override RouteData GetRouteData(HttpContextBase httpContext) {
		return null;
	}
 
	public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) {
		var query = from pair in values
				where pair.Value != null
				let routable = pair.Value as IUrlRoutable
				where routable != null
				select new { pair.Key, Value = routable.GetRouteParameter() };
 
		foreach (var pair in query.ToList()) {
			values[pair.Key] = pair.Value;
		}
 
		return null;
	}
}

Here we loop through each key-value pair in the RouteValueDictionary. If the value implements IUrlRoutable then we call GetRouteParameter on that object and replace the original value in the RouteValueDictionary with the result of this method.

Next, we have to add the fake route to the RouteCollection in Application_Start. Note that this must be the first route added so that it gets a chance to intercept the RouteValueDictionary.

public static void RegisterRoutes(RouteCollection routes) {
	//Add our fake route first
	routes.Add(new RoutePreParser());
 
	routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
	routes.MapRoute(
		"Default", // Route name
		"{controller}/{action}/{id}", // URL with parameters
		new { controller = "Home", action = "Index", id = "" } // Parameter defaults
	);
}

Now, in our controller action we change the Create action to store the Post instance in the route values:

[AcceptVerbs(HttpVerbs.Post), AutoCommit]
public ActionResult Create(Post post) {
	context.Posts.InsertOnSubmit(post);
	return RedirectToAction("Show", new{ id = post });
}

…and everything now works as expected.

To recap, this is the new chain of events:

  1. The IoC container creates our DataContext
  2. The controller is instantiated and the DataContext is passed to its constructor
  3. The Create action is invoked with a Post instance created by MVC’s DefaultModelBinder
  4. The post is attached to the DataContext by calling InsertOnSubmit
  5. A RedirectToRouteResult is created by calling “RedirectToAction” with a dictionary of route values. (The “Id” parameter is our new Post instance.)
  6. The AutoCommit attribute calls SubmitChanges to our DataContext
  7. The Post is written to the database
  8. The Post instance stored in the RouteValueDictionary automatically has its Id property updated
  9. ExecuteResult on RedirectToRouteResult is invoked
  10. Our RoutePreParser inspects the RouteValueDictionary.
  11. GetRouteParameter on Post is invoked, returning the now-populated Post Id
  12. The RoutePreParser removes the Post instance from the RouteValueDictionary
  13. The RoutePreParser inserts the Post Id as the “id” in the RouteValueDictionary
  14. The (now correct) redirect URL is generated by the RouteCollection
  15. The user’s browser is redirected to this URL