C# 如何使ModelBinder为参数返回null?
我有一个POCO,我用它作为MVC3中动作的参数。大概是这样的: 我的类型 我的行动 当前,C# 如何使ModelBinder为参数返回null?,c#,asp.net-mvc,model-binding,modelbinders,C#,Asp.net Mvc,Model Binding,Modelbinders,我有一个POCO,我用它作为MVC3中动作的参数。大概是这样的: 我的类型 我的行动 当前,query作为SearchData的实例传递,所有属性都为null。我更喜欢为query获取null,这样我就可以执行上面代码中的null检查 我可以随时查看ModelBinder.Any()或仅查看ModelBinder中的各个键,查看它是否获得了query的任何属性,但我不想使用反射来循环query的属性。此外,我只能使用ModelBinder.Any()检查查询是否是我的唯一参数。一旦我添加了其他参
query
作为SearchData
的实例传递,所有属性都为null
。我更喜欢为query
获取null
,这样我就可以执行上面代码中的null检查
我可以随时查看ModelBinder.Any()
或仅查看ModelBinder
中的各个键,查看它是否获得了query
的任何属性,但我不想使用反射来循环query
的属性。此外,我只能使用ModelBinder.Any()
检查查询是否是我的唯一参数。一旦我添加了其他参数,该功能就会中断
使用MVC3中当前的模型绑定功能,是否有可能获得将POCO参数返回null的行为?您需要实现自定义modelbinder来完成此操作。您只需扩展
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;
}
具体实施
这是绑定器的实际实现,如果所有属性都为null,那么绑定器将为模型返回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;
}
}
我不知道你具体问题的答案,但我可以想出一个解决办法。为什么不向
SearchData
类添加一个方法呢
public bool IsEmpty(){
return Property1 == null
&& Property2 == null
&& Property3 == null;
}
当然,如果您尝试在多个类型上执行此操作,则可能会变得单调乏味。在路线中,请尝试
new { controller = "Articles", action = "Index", query = UrlParameter.Optional }
实现自定义modelbinder,但使用接口确定对象是否为null。我喜欢这种模式有两个原因:
public class NullValueModelBinder : DefaultModelBinder, IModelBinder {
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) {
object model = base.BindModel(controllerContext, bindingContext);
if (model is INullValueModelBindable && (model as INullValueModelBindable).IsNull()){
return null;
}
return model;
}
}
public interface INullValueModelBindable {
bool IsNull();
}
将自定义模型绑定器作为参数的属性实现 注意:模型上的所有属性都必须为空
public class NullModelBinder : DefaultModelBinder
{
public override 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;
}
}
public class NullModelAttribute : CustomModelBinderAttribute
{
public override IModelBinder GetBinder()
{
return new NullModelBinder();
}
}
public ActionResult Index([NullModel] SearchData query)
{
// I'd like to be able to do this
if (query == null)
{
// do something
}
}
我发现
DefaultModelBinder
的SetProperty
只有在找到属性并尝试设置它时才会被调用
记住这一点,这是我的NullModelBinder
public class NullModelBinder : DefaultModelBinder
{
public bool PropertyWasSet { get; set; }
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object model = base.BindModel(controllerContext, bindingContext);
if (!PropertyWasSet)
{
return null;
}
return model;
}
protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, object value)
{
base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
PropertyWasSet = true;
}
}
因此,只有当框架在请求中找到属性并尝试将其设置为模型时,我才会返回由BindModel
创建的模型
注意:
我的方法不同于前面答案中的NullBinder,因为它只对每个属性执行一次,而在最坏的情况下,其他NullBinder执行两次
在此代码中,snnipet:
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object model = base.BindModel(controllerContext, bindingContext);
// loop through every property for the model in the metadata
//CODE HERE
}
当调用base.BindModel
时,.Net将遍历模型上的每个属性,试图找到它们并在创建的模型上设置它们
然后CustomModelBinder再次遍历每个属性,直到在请求中找到一个,在这种情况下返回由.Net创建的模型,否则返回null
因此,如果没有设置属性,我们将有效地对模型的每个属性进行两次检查。我可以,但我真的不愿意,因为我必须保持这一点。啊,是的,这是解决方案。我用我的具体实现更新了你的答案。我最终也继承了DefaultModelBinder,就像你建议的那样。
public ActionResult Index([NullModel] SearchData query)
{
// I'd like to be able to do this
if (query == null)
{
// do something
}
}
public class NullModelBinder : DefaultModelBinder
{
public bool PropertyWasSet { get; set; }
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object model = base.BindModel(controllerContext, bindingContext);
if (!PropertyWasSet)
{
return null;
}
return model;
}
protected override void SetProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, System.ComponentModel.PropertyDescriptor propertyDescriptor, object value)
{
base.SetProperty(controllerContext, bindingContext, propertyDescriptor, value);
PropertyWasSet = true;
}
}
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
object model = base.BindModel(controllerContext, bindingContext);
// loop through every property for the model in the metadata
//CODE HERE
}