In realtà mi piace solo la terza soluzione, la renderò una soluzione generica per tutti i ModelBinders, inserendola in un raccoglitore personalizzato che eredita da DefaultModelBinder
ed è configurato per essere il raccoglitore modello predefinito per l'applicazione MVC.
Quindi si dovrebbe fare in modo che questo nuovo DefaultModelBinder
associ automaticamente qualsiasi proprietà decorata con un attributo PropertyBinder
, utilizzando il tipo fornito nel parametro.
Ho avuto l'idea da questo eccellente articolo: http://aboutcode.net/2011/03/12/mvc-property-binder.html.
Sarò anche voi mostrare il mio prendere sulla soluzione:
mio 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();
}
}
}
mio IPropertyBinder
interfaccia:
namespace MyApp.Web.Mvc
{
interface IPropertyBinder
{
object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext, MemberDescriptor memberDescriptor);
}
}
mio PropertyBinderAttribute
:
namespace MyApp.Web.Mvc
{
public class PropertyBinderAttribute : Attribute
{
public PropertyBinderAttribute(Type binderType)
{
BinderType = binderType;
}
public Type BinderType { get; private set; }
}
}
Un esempio di un legante struttura:
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);
}
}
}
Esempio di sopra legante proprietà utilizzata:
namespace MyApp.Web.Models
{
public class MyModel
{
[PropertyBinder(typeof(TimeSpanBinder))]
public TimeSpan InspectionDate { get; set; }
}
}
Suppongo che questo approccio possa essere migliorato sostituendo 'GetPropertyValue'. Vedi [la mia risposta] (http://stackoverflow.com/a/19727630/129073) per i dettagli. – Gebb
È possibile avere un'implementazione di PropertyBinder simile per l'API Web? – mehul9595
'CreateBinder' non funziona con questo costruttore, quindi ho abbandonato' HttpContext' e ho usato 'var value = bindingContext.ValueProvider.GetValue (memberDescriptor.Name);' per ottenere il valore della proprietà. – Brad