ModelBindingContext ModelName - asp.net-mvc

Can anyone explain where the ModelName gets populated from?
Looked in MSDN documentation and no explaination here.
I am creating a custom model binder and within it I get null for the following:
var result = bindingContext.ModelName);

The ModelBindingContext object is created and populated by whoever calls into the BindModel() method. If the model is coming in as an argument to your action method, this is done by ControllerActionInvoker.GetParameterValue(), and the ModelName property will be set to the name of the parameter (unless overridden by [Bind(Prefix = ... )]).
If the model is being updated via UpdateModel(), the ModelBindingContext object is created by the UpdateModel() method itself. The ModelName parameter will be the prefix argument passed to UpdateModel().

Related

Is it possible to bind into Key/Value dictionary inputs in format InputName/InputValue

Is it possible to bind into Key/Value dictionary inputs in format InputName/InputValue?
If you want the values in the form come and sit as List<KeyValuePair<string,string>> in action methods you have to for custom model binder.
public class CustomModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var form = controllerContext.HttpContext.Request.Form;
return form.Cast<string>().Select(s => new KeyValuePair<string,string>(s, form[s])).ToList();
}
}
[HttpPost]
public ActionResult Save(List<KeyValuePair<string,string>> form)
{
...
}
If you are fine with other options you can go for FormCollection as #Tommy suggested. By the way FormCollection is a built-in dictionary class that contains all the values submitted by the form.
You can also create a simple class having properties with names as the form field names and the default model binder automatically instantiate and set the values for the class and provide to the action. This is the approach people usually follow.
MVC already does this if you pass the right parameter to your action method. Note - it is highly recommended to use strongly typed views and models in your action methods.
With that said, if your method signature uses the following:
public ActionResult MyMethod(FormsCollection MyFormValues)
Then in your code, you can use
var myFormValue = MyFormValues("InputName");
Where InputName is the key used from the FormsCollection which will give you the value that was set from the HTML.

Will a call to IModelBinder.BindModel ever have bindingContext.Model/ModelMetadata already set?

Is it safe to blindly overwrite the ModelMetaData of a binding context to set the type of the model? I'm worried the binder may be used to update pre-built models in edit scenarios and I will lose data if I just overwrite the ModelMetaData.
Sample code:
Public Class CustomModelBinder
Inherits DefaultModelBinder
Public Overrides Function BindModel(ByVal controllerContext As ControllerContext, ByVal bindingContext As ModelBindingContext) As Object
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(Nothing, GetSpecificModelTypeBasedOnBindingData(bindingContext))
Return MyBase.BindModel(controllerContext, bindingContext)
End Function
End Class
Yes, it is possible that the model will be already specified. This occurs, for example, when someone tries to update an existing model using TryUpdateModel/UpdateModel, as shown here.
public ActionResult Update(int id)
{
var modelToUpdate = GetExistingModel(id);
if (TryUpdateModel(modelToUpdate)) // replacing the Model or ModelBinderContext.Metadata in the model binder could have unexpected and unwanted results.
{
// etc.
}
// etc.
}

Can I automatically trigger model validation inside a custom model binder?

I have a complex object that I'm binding off of a form. The model binder looks like this:
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var form = new MyForm();
var myObject = ...; //try to load up the object
/* logic to populate values on myObject */
form.MyObject = myObject;
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, new ValueProviderResult(form, "", CultureInfo.CurrentUICulture));
return form;
}
and it is doing what it's supposed to; I get a correctly populated MyForm out of it, and a reference to the same MyForm instance is included in the ModelState. However, the form does not get validated using either the DataAnnotations or my CustomValidation validation. In order to cause that validation, I have to add a TryValidateModel() call in my Controller:
[HttpPost]
public ActionResult ProcessMyForm(MyForm form)
{
//ModelState has the MyForm instance inside of it
//TryValidateModel(ModelState); //this does not work
TryValidateModel(form); //this works
if (!ModelState.IsValid)
{
return View("Complete", form);
}
return RedirectToAction("Index");
}
Which not only calls into my custom validation, but also updates the value of ModelState.IsValid.
In addition to my title question, this raises a couple of questions:
Why does TryValidateModel(ModelState) not validate the form when ModelState has a reference to the same instance of the form that TryValidateModel(form) correctly validates?
Why does TryValidateModel(form) cause the value of ModelState.IsValid to be updated?
In general, why are the binders responsible for updating ModelState?
The ModelBinder's responsibility is to bind values from the request into the model(s) you are using.
The ModelState property is just a dictionary containing the current state of you models. Look at modelstate like an errorlist.
When you have a custom ModelBinder you map the values from the request into the class of your choice. That will end up as a parameter into your actionmethod.
I wouldn't agree with you that modelbinders are responsible for updating the ModelState since the ModelBinder is run when it binds the values, it can still have IsValid=true before you run TryValidateModel.
When you later run the TryValidateModel (or ValidateModel for that matter) it will update the ModelState property with whatever errors you have. You can also use different types to validation methods (DataAnnotations, IValidatableObject...)

How to update custom ModelBinder to work with altered ModelBindingContext.ValueProvider interface in ASP.NET MVC RC2

I have a custom model binder that takes a comma separated list and cleans out any empty values, then passes it along to the default model binder. This worked in ASP.NET MVC Preview 2, but when I upgraded to RC2, the below won't compile because the interface of ValueProvider only has a GetValue() method, no [] accessor. Is what I'm doing below possible through some other mechanism in the binding context? I'd rather not have to create a full blown model binder for such a simple situation. The main goal is when the values are bound to a List<SomeEnum>, any empty values are skipped.
public class EnumListModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var result = bindingContext.ValueProvider[bindingContext.ModelName];
string[] rawValues = (string[])result.RawValue;
var newValues = new List<string>();
foreach (string value in rawValues)
{
if (!String.IsNullOrEmpty(value))
{
newValues.Add(value);
}
}
string newValuesAttempted = String.Join(",", newValues.ToArray());
// overwrite the ValueProviderResult with the cleaned up csv list
// this is the part I'm not sure how to implement using the interface
bindingContext.ValueProvider[bindingContext.ModelName] =
new ValueProviderResult(newValues.ToArray(), newValuesAttempted, result.Culture);
return System.Web.Mvc.ModelBinders.Binders.DefaultBinder.BindModel(controllerContext, bindingContext);
}
}
What, exactly, is wrong with using GetValue() instead of [] here? It does the same thing. But ValueProvider is an interface now, and interfaces can't have indexers. Hence, GetValue().
Change your code to:
var result = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
I'm a little surprised that the cast on the next line ever worked, though. Seems highly dependent on what the user actually submits and what the particular property type is. That's unrelated to your question, though.
There wasn't much of a solution here now that the ValueProvider collection is readonly. Instead I ended up using a custom model binder
Is there a way to have the DefaultModelBinder ignore empty items when binding to a List<Enum>

Doing custom binding for a specific data-type in a ModelBinder

I'm in the process of creating my own custom ModelBinder that inherits from DefaultModelBinder, and manually binds XElement-typed properties.
Now it seems that I have to override the 'BindProperty' method, like so:
protected override void BindProperty(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
System.ComponentModel.PropertyDescriptor propertyDescriptor)
{
if (propertyDescriptor.PropertyType == typeof(XElement))
{
// code here to take the POST-ed form value and put it in the property as an XElement instance
}
else
base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
}
What code should I be using to:
A) get the value of the property from the posted Form values?
B) inject this value into the property?
I tried running Reflector on the DefaultModelBinder class to see how it does it, but the code was very confusing.
I need someone who's done this before to walk me through it.
The bindingContext parameter contains a ValueProvider property that is already populated with the values from the request. The idea is that you pull the values from that.
It is simply a dictionary of values, so you can index into it using the name of the field you would like to bind.
The easiest way to understand what's goint on is to apply your custom ModelBinder and then set a breakpoint in your code and inspect what data you got while in the debugger.

Resources