MikeW
MikeW

Reputation: 4809

ModelBinding in Mvc3 - model is recreated between actions via form post - how to persist

I have a multi step wizard that I have pieced together from useful posts like this its got a few problems however.. here's the setup I have

[Serializable]
public class WizardModel
{

    public IList<IStepViewModel> Steps 
    { 
        get; 
        set; 
    }
    public void Initialize()
    {
        Steps = typeof(IStepViewModel)
            .Assembly
            .GetTypes()
            .Where(t => !t.IsAbstract && typeof(IStepViewModel).IsAssignableFrom(t))
            .Select(t => (IStepViewModel)Activator.CreateInstance(t))
            .ToList();
    }

}

My Wizard controller

    public ActionResult Index()
    {
        var wizard = new WizardModel();     
        wizard.Initialize();
        //this populates wizard.Steps with 3 rows of IStepViewModel
        return View(rollover);       
    }

    [HttpPost]
    public ActionResult Index(
        [Deserialize] WizardModel wizard,
        IStepViewModel step
        )
    {
        //but when this runs wizard is a new class not the one previously Initialized
         wizard.Steps[rollover.CurrentStepIndex] = step;
    }

My issue is that wizard is a fresh object each time its posted - when I'm trying to pass the same model around populating each step in the array. Does anyone have an idea of where I'm going wrong here?

Here's the ModelBinding

Global.asax

   ModelBinders.Binders.Add(typeof(IStepViewModel), new FormTest.Models.StepViewModelBinder());

and

  public class StepViewModelBinder : DefaultModelBinder
{
    protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
    {
        var stepTypeValue = bindingContext.ValueProvider.GetValue("StepType");
        var stepType = Type.GetType((string)stepTypeValue.ConvertTo(typeof(string)), true);
        var step = Activator.CreateInstance(stepType);
        bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => step, stepType);
        return step;
    }
}

Thanks in advance

Edit:

if I understand, an alternative to using session is to serialize my model (below), and deserialize in my controller action. I set the values on the model that were posted to the controller .. which gets returned to the view for the next step and so on.. until the final step when I have a wizard model populated with each step.

Index.cshtml

   @using (Html.BeginForm())
   {
         @Html.Serialize("wizard", Model);  
         etc...
   }   

so the wizard parameter that I try deserialize here

    [Deserialize] WizardModel wizard,

comes through the controller post action is a new object each time - I would like to see if this is possible without using Session but @Html.Serialize? and Post

Upvotes: 3

Views: 589

Answers (3)

MikeW
MikeW

Reputation: 4809

This code did the job in the end. Inside the controller action,

        var serializer = new MvcSerializer();
        var value = Request["wizard"];
        var wizard = (WizardModel)serializer.Deserialize(value, SerializationMode.Signed);

And in the view

    @Html.Serialize("wizard", Model, SerializationMode.Signed);   

Upvotes: 1

Dave Thieben
Dave Thieben

Reputation: 5427

Model binding is used to bind POSTed form values to new objects. so if you want the exact same object, like Mike suggests, you will need to use Session or some other persistent store. however, if your objects can be recreated each time, then you just need to put enough data in the form so that when it gets POSTed, everything can be re-data-bound to get the same values in a new object.

Upvotes: 0

Mike Chaliy
Mike Chaliy

Reputation: 26648

Use session to persist object between requests.

Session["SuperWizard"] = wizard

Upvotes: 1

Related Questions