简体   繁体   中英

MVC custom model binder using the default binder for certain form values

I have a custom model binder which is invoked for a particular parameter going into an action method:

public override ActionResult MyAction(int someData, [ModelBinder(typeof(MyCustomModelBinder))]List<MyObject> myList ... )

This works well - the binder is called as expected. However, I want to invoke the default model binder for some addtional values that are in the Request.Form collection. The form keys are named like this:

dataFromView[0].Key
dataFromView[0].Value
dataFromView[1].Key
dataFromView[1].Value

The default model binder nicely converts these values into an IDictionary if I add an IDictionary as a parameter on the action method.

However, I want to manipulate these values at the model binder level (along with the original List object above).

Is there a way to get the default model binder to create this dictionary from the form values for my in the BindModel() method of my custom model binder?

public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
    //Get the default model binder to provide the IDictionary from the form values...           
}

I've tried to using the bindingContext.ValueProvider.GetValue but it always seems to return null when I'm trying to cast to an IDictionary.

Here's a potential solution I found (by looking at the default model binder source code) which allows you to use the default model binders functionality for creating a Dictionary, List etc.

Create a new ModelBindingContext detailing the binding values you require:

var dictionaryBindingContext = new ModelBindingContext()
            {
                ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => null, typeof(IDictionary<long, int>)),
                ModelName = "dataFromView", //The name(s) of the form elements you want going into the dictionary
                ModelState = bindingContext.ModelState,
                PropertyFilter = bindingContext.PropertyFilter,
                ValueProvider = bindingContext.ValueProvider
            };

var boundValues = base.BindModel(controllerContext, dictionaryBindingContext);

Now the default model binder is invoked with the binding context you have specified and will return the bound object as normal.

Seems to work so far...

If your model binder needs to work with some other form data this means that you haven't positioned this model binder on the correct type. The correct type for your model binder would be the following:

public class MyViewModel
{
    public IDictionary<string, string> DataFromView { get; set; }
    public List<MyObject> MyList { get; set; }
}

and then:

public class MyCustomModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var model = base.BindModel(controllerContext, bindingContext);

        // do something with the model

        return model;
    }
}

and then:

[HttpPost]
public ActionResult Index([ModelBinder(typeof(MyCustomModelBinder))] MyViewModel model)
{
    ...
}

This assumes the following data is posted:

dataFromView[0].Key
dataFromView[0].Value
dataFromView[1].Value
dataFromView[1].Key
myList[0].Text
myList[1].Text
myList[2].Text

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM