Search code examples
c#asp.net-mvc-3custom-model-binder

A created a new CustomModelBinder from one that already worked. Why does the new one never get called to do any binding?


Can I do something like this?

[HttpPost]
public ActionResult Index(WizardViewModel wizard, IStepViewModel step)
{

Where I have the following in my global.asax.cs application_start

    ModelBinders.Binders.Add(typeof(IStepViewModel), new StepViewModelBinder());
    ModelBinders.Binders.Add(typeof(WizardViewModel), new WizardViewModelBinder());

Update

So, I tried to see what is wrong. Here is my new code. It seems that the problem is with this WizardViewModel and it's binder. What "tells" the application to expect and incoming Wizard model?

[HttpPost]
public ActionResult Index(WizardViewModel wizard)
{

Where I have the following in my global.asax.cs application_start

    ModelBinders.Binders.Add(typeof(WizardViewModel), new WizardViewModelBinder());

Complete Binder Code

namespace Tangible.Binders
{
    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; 
        }
    }

    public class WizardViewModelBinder : DefaultModelBinder
    {
        protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
        {
                var wizardValue = bindingContext.ValueProvider.GetValue("wizard");
                if (wizardValue != null)
                {
                    var wizardType = Type.GetType((string)wizardValue.ConvertTo(typeof(string)), true);
                    var wizard = Activator.CreateInstance(wizardType);

                    bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => wizard, wizardType);
                    return wizard;
                }
                else
                {
                    var wizard = new Tangible.Models.WizardViewModel();
                    wizard.Initialize();
                    return wizard;
                }
        }
    }
}

Solution

  • The answer is simple - Yes! That is what you SHOULD do when you've got custom logic for binding values to your parameters. You can even do that with the use of ModelBinderAttribute, set on each of parameters individually.

        [HttpPost]
        public ActionResult Index([ModelBinder(typeof(WizardViewModelBinder))]WizardViewModel wizard, 
    [ModelBinder(typeof(StepViewModelBinder))]IStepViewModel step)
        { }
    

    And as I see, the mistake is in your model binder code. I don't have time to check it up, but as far as i remember, CreateModel is used by model binder to create instance of the model, and then that returned instance is model-binded. So, override BindModel instead of CreateModel and write your model binding logic in BindModel. That definitely works.

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    {
    //your model binding logic here
    }