# 10 Kasım 2008 Pazartesi
I'm sure someone did this before, but I'm too lazy to search for it :)

Model Binders in MVC is a cool concept to map your form values to parameter objects of your Action. There's also this automatic error message display capabilities in MVC. All explained by The Gu here.

Here's my take on validating a model object during the binding process. Castle project has a validation framework and a range of validators which can be used standalone. It would be cool to use it with MVC to ease the validation pain a bit, huh?

First, we need a binder to validate the object using Castle's ValidatorRunner:

    using System.Web.Mvc;
    using Castle.Components.Validator;
    public class ValidatingModelBinder : DefaultModelBinder
        public override ModelBinderResult BindModel(ModelBindingContext bindingContext)
            var result = base.BindModel(bindingContext);
            if (result != null && result.Value != null)
                var runner = new ValidatorRunner(new CachedValidationRegistry());
                if (!runner.IsValid(result.Value))
                    var summary = runner.GetErrorSummary(result.Value);
                    foreach (var invalidProperty in summary.InvalidProperties)
                        foreach (var error in summary.GetErrorsForProperty(invalidProperty))
                            bindingContext.ModelState.AddModelError(bindingContext.ModelName + "." + invalidProperty, error);
            return result;

And an object to validate:

    using Castle.Components.Validator;
    public class CreateUser
        [ValidateNonEmpty("Please enter user name.")]
        public string UserName { get; set; }
        [ValidateNonEmpty("Please enter password.")]
        public string Password  { get; set; }
        [ValidateEmail("Email is not valid.")]
        [ValidateNonEmpty("Please enter email address.")]
        public string Email { get; set; }

And an action to use the object:

        public ActionResult CreateUser(CreateUser createUser)
            if (ViewData.ModelState.IsValid)
                ViewData["Message"] = "Done.";
            return View();

Please note that, when the action is invoked, parameter is already validated. We just check ViewData.ModelState.IsValid and act accordingly.

Next, we'll tell the MVC engine to bind CreateUser objects through ValidatingModelBinder, in Application_Start() of Global.asax.cs:

        ModelBinders.Binders.Add(typeof(CreateUser), new ValidatingModelBinder());

So, whenever the engine is bindign to a CreateUser, our binder will execute (and validate) the object. Finally, code below for the view:

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <%if (ViewData["Message"] != null)
    <%} %>
    <%=Html.ValidationSummary() %>
    <form method="post" action="/Home/CreateUser">
        User name: <br />
        <%=Html.TextBox("CreateUser.UserName")%>  <%=Html.ValidationMessage("CreateUser.UserName", "*")%><br />
        Password: <br />
        <%=Html.Password("CreateUser.Password")%> <%=Html.ValidationMessage("CreateUser.Password", "*")%><br />
        Email: <br />
        <%=Html.TextBox("CreateUser.Email")%> <%=Html.ValidationMessage("CreateUser.Email", "*")%><br />
        <input type="submit" value="Submit" />

Here's the result:

posted on 10 Kasım 2008 Pazartesi 10:40:43 UTC  #   
# 04 Kasım 2008 Salı
Update: Check the comment by Matt Hinze. It appears that we don't need this kind of complexity after Preview 4. Check this and this, for example. Thanks Matt. My lesson? When you're using pre-release software with lots of changes and improvements in each release, be sure to reevaluate all your past assumptions in each.


Just a note. I use a base like this code to avoid repetitive test setup when testing MVC controllers.

    using System;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;
    using MbUnit.Framework;
    using Rhino.Commons;
    using Rhino.Mocks;
    public abstract class ControllerTestBase<TController> 
        where TController : Controller
        protected TController controller;
        protected ControllerContext controllerContext;
        protected IDisposable disposeGlobalUnitOfWorkRegistration;
        protected HttpContextBase httpContextStub;
        protected HttpSessionStateBase httpSessionStub;
        protected IUnitOfWork unitOfWorkStub;
        public virtual void Setup()
            unitOfWorkStub = MockRepository.GenerateStub<IUnitOfWork>();
            httpContextStub = MockRepository.GenerateStub<HttpContextBase>();
            httpSessionStub = MockRepository.GenerateStub<HttpSessionStateBase>();
            httpContextStub.Stub(x => x.Session).Return(httpSessionStub);
            var httpRequestStub = MockRepository.GenerateStub<HttpRequestBase>();
            httpContextStub.Stub(x => x.Request).Return(httpRequestStub);
            controllerContext = new ControllerContext(httpContextStub, new RouteData(), controller);
            controller.ControllerContext = controllerContext;
            disposeGlobalUnitOfWorkRegistration = UnitOfWork.RegisterGlobalUnitOfWork(unitOfWorkStub);
        protected abstract void SetupController();
        public void TearDown()


    using System.Web.Mvc;
    using Common;
    using Core.Authentication;
    using Core.Repositories;
    using Domain.Entities;
    using MbUnit.Framework;
    using Rhino.Commons;
    using Rhino.Mocks;
    using UI.Controllers;
    using UI.Properties;
    public class RegisterControllerTests : ControllerTestBase<RegisterController>
        private IRepository<User> repositoryStub;
        private IAuthenticationService authServiceStub;
        protected override void SetupController()
            repositoryStub = MockRepository.GenerateStub<UserRepository>();
            authServiceStub = MockRepository.GenerateStub<IAuthenticationService>();
            controller = new RegisterController(repositoryStub, authServiceStub);
        public void Will_display_error_message_if_logged_in()
            authServiceStub.Stub(c => c.IsAuthenticated()).Return(true);
            var result = controller.Index() as RedirectToRouteResult;
            var message = controller.TempData["Message"] as string;
            Assert.AreEqual(GlobalResources.CannotRegisterAlreadyRegistered, message);
            Assert.AreEqual(result.Values["action"], "Index");
            Assert.AreEqual(result.Values["controller"], "Message");
posted on 04 Kasım 2008 Salı 20:32:02 UTC  #