MVC UpdateModel when the names don't match up

asp.net-mvc, updatemodel

Solution

I would suggest you a custom model binder:

using System;
using System.Globalization;
using System.Web.Mvc;

public class MyClassBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        var model = (MyClass)base.CreateModel(controllerContext, bindingContext, modelType);

        var day = bindingContext.ValueProvider["MyDate-Day"];
        var month = bindingContext.ValueProvider["MyDate-Month"];
        var year = bindingContext.ValueProvider["MyDate-Year"];

        var dateStr = string.Format("{0}/{1}/{2}", month.AttemptedValue, day.AttemptedValue, year.AttemptedValue);
        DateTime date;
        if (DateTime.TryParseExact(dateStr, "MM/dd/yyyy", null, DateTimeStyles.None, out date))
        {
            model.MyDate = date;
        }
        else
        {
            bindingContext.ModelState.AddModelError("MyDate", "MyDate has invalid format");
        }

        bindingContext.ModelState.SetModelValue("MyDate-Day", day);
        bindingContext.ModelState.SetModelValue("MyDate-Month", month);
        bindingContext.ModelState.SetModelValue("MyDate-Year", year);

        return model;
    }
}

This simplifies your controller action to:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult MyAction(MyClass myClass)
{
    if (!ModelState.IsValid)
    {
        return View(myClass);
    }
    // Do something with myClass
    return RedirectToAction("success");
}

And register the binder in Global.asax:

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);
    ModelBinders.Binders.Add(typeof(MyClass), new MyClassBinder());
}

Problem

Let's say that you have a Model that looks kind of like this: ``` public class MyClass { public string Name { get; set; } public DateTime MyDate { get; set; } } ``` The default edit template that Visual Studio gives you is a plain textbox for the `MyDate` property. This is all fine and good, but let's say that you need to split that up into it's Month/Day/Year components, and your form looks like: ``` <label for="MyDate">Date:</label> <%= Html.TextBox("MyDate-Month", Model.MyDate.Month) %> <%= Html.TextBox("MyDate-Day", Model.MyDate.Day) %> <%= Html.TextBox("MyDate-Year", Model.MyDate.Year) %> ``` When this is submitted, a call to `UpdateModel` won't work, since there isn't a definition for `MyDate-Month`. Is there a way to add a custom binder to the project to handle situations like this, or if the HTML inputs are named differently (for whatever reasons)? One workaround I've found is to use JavaScript to inject a hidden input into the form before submission that concatenates the fields and is named properly, but that feels wrong.

Original source