![](/img/trans.png)
[英]Can I use ModelBinder to get object without adding parameter to action?
[英]How do I make the ModelBinder return null for a parameter?
我有一個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
。 我更喜歡我的query
為null
,所以我可以在上面的代碼中進行null檢查。
我總是看ModelBinder.Any()
或只是在各個鍵ModelBinder
,看它是否有任何的屬性query
,但我不希望有過的屬性,使用反射來循環query
。 另外,我只能使用ModelBinder.Any()
檢查查詢是否是我唯一的參數。 只要我添加其他參數,該功能就會中斷。
使用MVC3中的當前模型綁定功能,是否可以獲得將POCO參數返回null的行為?
您需要實現自定義模型綁定器才能執行此操作。 您可以只擴展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;
}
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
ModelBinders.Binders.Add(typeof(SearchData), new SearchDataModelBinder());
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
MvcHandler.DisableMvcResponseHeader = true;
}
在路線嘗試
new { controller = "Articles", action = "Index", query = UrlParameter.Optional }
將自定義模型綁定器實現為參數的屬性。
注意:模型上的所有屬性都必須可以為空
這是上面的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; } }
創建屬性
public class NullModelAttribute : CustomModelBinderAttribute { public override IModelBinder GetBinder() { return new NullModelBinder(); } }
在控制器方法上使用Attribute
public ActionResult Index([NullModel] SearchData query) { // I'd like to be able to do this if (query == null) { // do something } }
我不知道你的具體問題的答案,但我可以想到一個解決方法。 為什么不直接向SearchData
類添加方法?
public bool IsEmpty(){
return Property1 == null
&& Property2 == null
&& Property3 == null;
}
當然,如果您嘗試使用多種類型,則可能會變得乏味。
實現自定義模型綁定器,但使用接口來確定對象是否為空。 我更喜歡這種模式有兩個原因:
它封裝了如何確定對象是否為該對象的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(); }
我發現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
返回由BindModel
創建的模型。
注意:
我的方法與之前答案的NullBinders不同,因為它只在每個屬性中進行一次,而在最壞的情況下,其他NullBinders進行兩次。
在這段代碼中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。
因此,如果沒有設置屬性,我們將有效地遍歷模型的每個屬性兩次。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.