4

我有一个 POCO,我将其用作 MVC3 中某个操作的参数。像这样的东西:

我的风格

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
    }
}

目前,query作为 的实例传递,SearchData所有属性都为null。我希望我得到一个nullfor,query这样我就可以进行上面代码中的空值检查。

我总是可以查看ModelBinder.Any()或只查看各种键ModelBinder以查看它是否具有 的任何属性query,但我不想使用反射来循环query. 另外,我只能使用ModelBinder.Any()检查查询是否是我唯一的参数。一旦我添加其他参数,该功能就会中断。

使用 MVC3 中当前的模型绑定功能,是否可以获得将 POCO 参数返回 null 的行为?

4

6 回答 6

6

您需要实现一个自定义模型绑定器来执行此操作。你可以只扩展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;
}

具体实施

这是绑定器的实际实现,如果所有属性都为空,将为模型返回空。

/// <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;
    }
}

将此作为活页夹添加到 global.asax

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    ModelBinders.Binders.Add(typeof(SearchData), new SearchDataModelBinder());
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    MvcHandler.DisableMvcResponseHeader = true;
}
于 2012-07-13T17:54:47.223 回答
1

在路线尝试

new { controller = "Articles", action = "Index", query = UrlParameter.Optional }
于 2012-07-13T17:55:31.183 回答
1

将自定义模型绑定器实现为参数的属性。

注意:模型上的所有属性都必须可以为空

  1. 这是上面的 ModelBinderClass

    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;
         }
    }
    
  2. 创建属性

    public class NullModelAttribute : CustomModelBinderAttribute
    {
        public override IModelBinder GetBinder()
        {
            return new NullModelBinder();
        }
    }
    
  3. 在控制器方法上使用属性

    public ActionResult Index([NullModel] SearchData query)
    {
        // I'd like to be able to do this
        if (query == null)
        {
            // do something
        }
    }
    
于 2016-01-19T05:46:37.187 回答
0

我不知道您的具体问题的答案,但我可以想到一个解决方法。为什么不给SearchData类添加一个方法呢?

public bool IsEmpty(){
  return Property1 == null 
      && Property2 == null 
      && Property3 == null;
}

当然,如果您尝试在不止一种类型上执行此操作,则可能会变得乏味。

于 2012-07-13T17:54:55.397 回答
0

实现自定义模型绑定器,但使用接口来确定对象是否为空。我更喜欢这种模式有两个原因:

  1. 在每个绑定上使用反射可能会非常昂贵
  2. 它封装了如何确定一个对象对该对象是否为空的逻辑。

    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();
    }
    
于 2014-05-20T11:57:41.290 回答
0

我发现当它找到属性并尝试设置它时,唯一的被调用SetPropertyDefaultModelBinder

考虑到这一点,这是我的 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.

笔记:

我的方法与先前答案的 NullBinders 不同,因为它只遍历每个属性一次,而在最坏的情况下,其他 NullBinders 会遍历两次。

在这段代码片段中:

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。

因此,如果没有设置属性,我们将有效地检查模型的每个属性两次。

于 2018-02-22T18:12:58.237 回答