Вы можете попробовать что-то из строк:
// Just a marker attribute
public class CurrencyAttribute : Attribute
{
}
public class MyViewModel
{
[Currency]
public double MyCurrencyValue { get; set; }
}
public class CurrencyBinder : DefaultModelBinder
{
protected override object GetPropertyValue(
ControllerContext controllerContext,
ModelBindingContext bindingContext,
PropertyDescriptor propertyDescriptor,
IModelBinder propertyBinder)
{
var currencyAttribute = propertyDescriptor.Attributes[typeof(CurrencyAttribute)];
// Check if the property has the marker attribute
if (currencyAttribute != null)
{
// TODO: improve this to handle prefixes:
var attemptedValue = bindingContext.ValueProvider
.GetValue(propertyDescriptor.Name).AttemptedValue;
return SomeMagicMethodThatParsesTheAttemptedValue(attemtedValue);
}
return base.GetPropertyValue(
controllerContext,
bindingContext, propertyDescriptor,
propertyBinder
);
}
}
public class HomeController: Controller
{
[HttpPost]
public ActionResult Index([ModelBinder(typeof(CurrencyBinder))] MyViewModel model)
{
return View();
}
}
UPDATE:
Вот улучшение связующего (см. Раздел TODO
в предыдущем коде):
if (!string.IsNullOrEmpty(bindingContext.ModelName))
{
var attemptedValue = bindingContext.ValueProvider
.GetValue(bindingContext.ModelName).AttemptedValue;
return SomeMagicMethodThatParsesTheAttemptedValue(attemtedValue);
}
Для обработки коллекций вам необходимо зарегистрировать подшивку в Application_Start
, поскольку вы больше не сможете украшать список с помощью ModelBinderAttribute
:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
ModelBinders.Binders.Add(typeof(MyViewModel), new CurrencyBinder());
}
И тогда ваше действие может выглядеть так:
[HttpPost]
public ActionResult Index(IList<MyViewModel> model)
{
return View();
}
Подводя итог важной части:
bindingContext.ValueProvider.GetValue(bindingContext.ModelName)
Дальнейшим шагом по улучшению этого связывателя будет обработка валидации (AddModelError / SetModelValue)