Woah, Nellie.NET!

From my simple mind to… mine…

ASP.NET MVC Model Binders

leave a comment »

Model binders are a great way to allow the ASP.NET MVC framework to bind the passed request or query string data to a model object.

For more information start here with Scott Gu’s blog:

http://weblogs.asp.net/scottgu/archive/2008/09/02/asp-net-mvc-preview-5-and-form-posting-scenarios.aspx

I found this… the fourth post down by freechoice being most significant:

http://forums.asp.net/p/1317611/2611395.aspx

There is code in there for a dynamic model binder, that is a great solution.

I threw together a quick test and sample model binder just to spike out my options:

#region

using System.Collections.Specialized;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using LinearJunction.Leads.Domain;
using NUnit.Framework;
using NUnit.Framework.SyntaxHelpers;
using Rhino.Mocks;

#endregion

namespace LinearJunction.Leads.Web.Specs
{
    public class LeadModelBinderSpecs
    {
        #region Nested type: When_lead_form_data_is_submitted

        [TestFixture]
        public class When_lead_form_data_is_submitted
        {
            #region Setup/Teardown

            [SetUp]
            public void Setup_Context()
            {
                SetupMocks();
                SetupExpectedRequestData();
                SetupExpectations();

                ControllerContext controllerContext = new ControllerContext(httpContextBase, new RouteData(), controllerBase);
                boundLead = new LeadModelBinder().GetValue(controllerContext, "Lead", typeof (Lead), modelStateDictionary);
            }

            #endregion

            private void SetupExpectations()
            {
                httpContextBase.Expect(context => context.Request).Return(requestBase).Repeat.Times(4);
                requestBase.Expect(request => request.QueryString).Return(null).Repeat.Times(4);
                requestBase.Expect(request => request.Form).Return(nameValueCollection).Repeat.Times(12);

                mockRepository.ReplayAll();
            }

            private void SetupMocks()
            {
                mockRepository = new MockRepository();
                controllerBase = mockRepository.DynamicMock<ControllerBase>();
                httpContextBase = mockRepository.StrictMock<HttpContextBase>();
                modelStateDictionary = mockRepository.PartialMock<ModelStateDictionary>();
                requestBase = mockRepository.StrictMock<HttpRequestBase>();
            }

            private void SetupExpectedRequestData()
            {
                nameValueCollection = new NameValueCollection();
                nameValueCollection.Add("FirstName", "Joe");
                nameValueCollection.Add("LastName", "Bob");
                nameValueCollection.Add("EmailAddress", "foo@nowhere.com");
                nameValueCollection.Add("PhoneNumber", "2102221234");
            }

            private object boundLead;
            private ModelStateDictionary modelStateDictionary;
            private NameValueCollection nameValueCollection;
            private ControllerBase controllerBase;
            private HttpRequestBase requestBase;
            private HttpContextBase httpContextBase;
            private MockRepository mockRepository;

            [Test]
            public void Should_return_new_lead()
            {
                Assert.That(boundLead, Is.Not.Null);
                Assert.That(boundLead, Is.InstanceOfType(typeof (Lead)));
            }

            [Test]
            public void Should_have_lead_data_set()
            {
                Lead lead = boundLead as Lead;
                Assert.That(lead.EmailAddress, Is.EqualTo("foo@nowhere.com"));
                Assert.That(lead.FirstName, Is.EqualTo("Joe"));
                Assert.That(lead.LastName, Is.EqualTo("Bob"));
                Assert.That(lead.PhoneNumber, Is.EqualTo("2102221234"));
                Assert.That(lead.LeadStatus, Is.EqualTo(LeadStatus.NEW));
            }
        }

        #endregion
    }
}

And the resulting class:

#region

using System;
using System.Web.Mvc;
using LinearJunction.Leads.Domain;

#endregion

namespace LinearJunction.Leads.Web
{
    public class LeadModelBinder : DefaultModelBinder
    {
        public override object GetValue(ControllerContext controllerContext, string modelName, Type modelType, ModelStateDictionary modelState)
        {
            string firstName = (string) base.GetValue(controllerContext, "FirstName", typeof(string), modelState);
            string lastName = (string)base.GetValue(controllerContext, "LastName", typeof(string), modelState);
            string emailAddress = (string)base.GetValue(controllerContext, "EmailAddress", typeof(string), modelState);
            string phoneNumber = (string)base.GetValue(controllerContext, "PhoneNumber", typeof(string), modelState);

            return new LeadFactory().CreateNewFrom(firstName, lastName, emailAddress, phoneNumber);
        }
    }
}

I’ll go ahead and register it on application startup.


        protected void Application_Start(object sender, EventArgs e)
        {
            ModelBinders.Binders[typeof (Lead)] = new LeadModelBinder();

            RegisterRoutes(RouteTable.Routes);
            InitializeWindsor();
        }

Finally, note that I used this approach because of my use of a factory to create the new lead. It’s a great way to handle a significant aspect. If you have no additional requirements outside of binding form data to your model object, you can use the complex binder provided by the MVC framework.

Now, something like this should just work, as the form data will be bound to the Lead instance and passed into the controller’s Submit method:


#region

using System.Web.Mvc;
using LinearJunction.Leads.Domain;

#endregion

namespace tbdnetwork.Web.LeadCapture.Controllers
{
    public class LeadController : Controller
    {
        private readonly LeadRepository leadRepository;

        public LeadController(LeadRepository leadRepository)
        {
            this.leadRepository = leadRepository;
        }

        [AcceptVerbs("POST")]
        public ActionResult Submit(Lead lead)
        {
            leadRepository.Save(ref lead);
            return RedirectToAction("SubmissionStatus");
        }
    }
}

Let me go test it and find out! 😉

Advertisements

Written by Nelson

September 24, 2008 at 6:20 am

Posted in .NET

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: