I've got a POCO that I'm using as an argument to an action in MVC3. Something like this:
public class SearchData
{
public string Property1 { get; set; }
public string Property2 { get; set; }
public string Property3 { get; set; }
}
public ActionResult Index(SearchData query)
{
// I'd like to be able to do this
if (query == null)
{
// do something
}
}
Currently, query
is passed as an instance of SearchData
with all of the properties as null
. I'd prefer that i get a null
for query
so I can just do the null check that I have in the above code.
I could always look at ModelBinder.Any()
or just the various keys in ModelBinder
to see if it got any of the properties for query
, but I don't want to have to use reflection to loop over the properties of query
. Also, I can only use the ModelBinder.Any()
check if query is my only parameter. As soon as I add additional parameters, that functionality breaks.
With the current model binding functionality in MVC3, is it possible to get the behavior of returning null for POCO argument to an action?
You'll need to implement a custom modelbinder to do this. You can just extend DefaultModelBinder
.
public override object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
object model = base.BindModel(controllerContext, bindingCOntext);
if (/* test for empty properties, or some other state */)
{
return null;
}
return model;
}
This is the actual implementation of the binder that will return null for the model if all of the properties are null.
/// <summary>
/// Model binder that will return null if all of the properties on a bound model come back as null
/// It inherits from DefaultModelBinder because it uses the default model binding functionality.
/// This implementation also needs to specifically have IModelBinder on it too, otherwise it wont get picked up as a Binder
/// </summary>
public class SearchDataModelBinder : DefaultModelBinder, IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// use the default model binding functionality to build a model, we'll look at each property below
object model = base.BindModel(controllerContext, bindingContext);
// loop through every property for the model in the metadata
foreach (ModelMetadata property in bindingContext.PropertyMetadata.Values)
{
// get the value of this property on the model
var value = bindingContext.ModelType.GetProperty(property.PropertyName).GetValue(model, null);
// if any property is not null, then we will want the model that the default model binder created
if (value != null)
return model;
}
// if we're here then there were either no properties or the properties were all null
return null;
}
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
ModelBinders.Binders.Add(typeof(SearchData), new SearchDataModelBinder());
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
MvcHandler.DisableMvcResponseHeader = true;
}