When I use the default model binding to bind form parameters to a complex object which is a parameter to an action, the framework remembers the values passed to the first request, meaning that any subsequent request to that action gets the same data as the first. The parameter values and validation state are persisted between unrelated web requests.
Here is my controller code (service
represents access to the back end of the app):
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Create()
{
return View(RunTime.Default);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(RunTime newRunTime)
{
if (ModelState.IsValid)
{
service.CreateNewRun(newRunTime);
TempData["Message"] = "New run created";
return RedirectToAction("index");
}
return View(newRunTime);
}
My .aspx view (strongly typed as ViewPage<RunTime
>) contains directives like:
<%= Html.TextBox("newRunTime.Time", ViewData.Model.Time) %>
This uses the DefaultModelBinder
class, which is meant to autobind my model's properties.
I hit the page, enter valid data (e.g. time = 1). The app correctly saves the new object with time = 1. I then hit it again, enter different valid data (e.g. time = 2). However the data that gets saved is the original (e.g. time = 1). This also affects validation, so if my original data was invalid, then all data I enter in the future is considered invalid. Restarting IIS or rebuilding my code flushes the persisted state.
I can fix the problem by writing my own hard-coded model binder, a basic naive example of which is shown below.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([ModelBinder(typeof (RunTimeBinder))] RunTime newRunTime)
{
if (ModelState.IsValid)
{
service.CreateNewRun(newRunTime);
TempData["Message"] = "New run created";
return RedirectToAction("index");
}
return View(newRunTime);
}
internal class RunTimeBinder : DefaultModelBinder
{
public override ModelBinderResult BindModel(ModelBindingContext bindingContext)
{
// Without this line, failed validation state persists between requests
bindingContext.ModelState.Clear();
double time = 0;
try
{
time = Convert.ToDouble(bindingContext.HttpContext.Request[bindingContext.ModelName + ".Time"]);
}
catch (FormatException)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName + ".Time", bindingContext.HttpContext.Request[bindingContext.ModelName + ".Time"] + "is not a valid number");
}
var model = new RunTime(time);
return new ModelBinderResult(model);
}
}
Am I missing something? I don't think it's a browser session problem as I can reproduce the problem if the first data is entered in one browser and the second in another.