rre*_*ejc 32 c# asp.net-mvc model-binding custom-model-binder
我有以下控制器操作:
[HttpPost]
public ViewResult DoSomething(MyModel model)
{
// do something
return View();
}
Run Code Online (Sandbox Code Playgroud)
哪里MyModel是这样的:
public class MyModel
{
public string PropertyA {get; set;}
public IList<int> PropertyB {get; set;}
}
Run Code Online (Sandbox Code Playgroud)
所以DefaultModelBinder应该没有问题地绑定它.唯一的事情是我想使用特殊/自定义绑定器进行绑定PropertyB,我也想重用这个绑定器.所以我认为解决方案是在PropertyB之前放置一个ModelBinder属性,当然这不起作用(属性上不允许使用ModelBinder属性).我看到两个解决方案:
要在每个属性上使用动作参数而不是整个模型(我不喜欢,因为模型具有很多属性),如下所示:
public ViewResult DoSomething(string propertyA, [ModelBinder(typeof(MyModelBinder))] propertyB)
Run Code Online (Sandbox Code Playgroud)要创建一个新类型,可以说MyCustomType: List<int>并注册此类型的模型绑定器(这是一个选项)
也许为MyModel创建一个绑定器,覆盖BindProperty以及该属性是否"PropertyB"使用我的自定义绑定器绑定该属性.这可能吗?
还有其他解决方案吗?
que*_*en3 19
覆盖BindProperty,如果属性为"PropertyB",则将该属性与我的自定义绑定器绑定
这是一个很好的解决方案,但不是检查"是PropertyB",而是更好地检查您自己定义属性级绑定器的自定义属性,例如
[PropertyBinder(typeof(PropertyBBinder))]
public IList<int> PropertyB {get; set;}
Run Code Online (Sandbox Code Playgroud)
您可以在此处查看BindProperty覆盖的示例.
Jon*_*han 17
我实际上喜欢你的第三个解决方案,我将把它作为所有ModelBinder的通用解决方案,将它放在一个自定义绑定器中,该绑定器继承DefaultModelBinder并配置为MVC应用程序的默认模型绑定器.
然后,您将使用参数中提供的类型使新的DefaultModelBinder自动绑定PropertyBinder使用属性修饰的任何属性.
我从这篇优秀的文章中得到了这个想法:http://aboutcode.net/2011/03/12/mvc-property-binder.html.
我还将向您展示我对解决方案的看法:
我的DefaultModelBinder:
namespace MyApp.Web.Mvc
{
public class DefaultModelBinder : System.Web.Mvc.DefaultModelBinder
{
protected override void BindProperty(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
PropertyDescriptor propertyDescriptor)
{
var propertyBinderAttribute = TryFindPropertyBinderAttribute(propertyDescriptor);
if (propertyBinderAttribute != null)
{
var binder = CreateBinder(propertyBinderAttribute);
var value = binder.BindModel(controllerContext, bindingContext, propertyDescriptor);
propertyDescriptor.SetValue(bindingContext.Model, value);
}
else // revert to the default behavior.
{
base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
}
}
IPropertyBinder CreateBinder(PropertyBinderAttribute propertyBinderAttribute)
{
return (IPropertyBinder)DependencyResolver.Current.GetService(propertyBinderAttribute.BinderType);
}
PropertyBinderAttribute TryFindPropertyBinderAttribute(PropertyDescriptor propertyDescriptor)
{
return propertyDescriptor.Attributes
.OfType<PropertyBinderAttribute>()
.FirstOrDefault();
}
}
}
Run Code Online (Sandbox Code Playgroud)
我的IPropertyBinder界面:
namespace MyApp.Web.Mvc
{
interface IPropertyBinder
{
object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext, MemberDescriptor memberDescriptor);
}
}
Run Code Online (Sandbox Code Playgroud)
我的PropertyBinderAttribute:
namespace MyApp.Web.Mvc
{
public class PropertyBinderAttribute : Attribute
{
public PropertyBinderAttribute(Type binderType)
{
BinderType = binderType;
}
public Type BinderType { get; private set; }
}
}
Run Code Online (Sandbox Code Playgroud)
属性绑定器的示例:
namespace MyApp.Web.Mvc.PropertyBinders
{
public class TimeSpanBinder : IPropertyBinder
{
readonly HttpContextBase _httpContext;
public TimeSpanBinder(HttpContextBase httpContext)
{
_httpContext = httpContext;
}
public object BindModel(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
MemberDescriptor memberDescriptor)
{
var timeString = _httpContext.Request.Form[memberDescriptor.Name].ToLower();
var timeParts = timeString.Replace("am", "").Replace("pm", "").Trim().Split(':');
return
new TimeSpan(
int.Parse(timeParts[0]) + (timeString.Contains("pm") ? 12 : 0),
int.Parse(timeParts[1]),
0);
}
}
}
Run Code Online (Sandbox Code Playgroud)
使用上述属性绑定器的示例:
namespace MyApp.Web.Models
{
public class MyModel
{
[PropertyBinder(typeof(TimeSpanBinder))]
public TimeSpan InspectionDate { get; set; }
}
}
Run Code Online (Sandbox Code Playgroud)
@ jonathanconway的答案很棒,但我想补充一点细节.
最好覆盖GetPropertyValue方法而不是BindProperty为了给出有效DefaultBinder机会的验证机制.
protected override object GetPropertyValue(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
PropertyDescriptor propertyDescriptor,
IModelBinder propertyBinder)
{
PropertyBinderAttribute propertyBinderAttribute =
TryFindPropertyBinderAttribute(propertyDescriptor);
if (propertyBinderAttribute != null)
{
propertyBinder = CreateBinder(propertyBinderAttribute);
}
return base.GetPropertyValue(
controllerContext,
bindingContext,
propertyDescriptor,
propertyBinder);
}
Run Code Online (Sandbox Code Playgroud)