Reputation: 34089
In asp.net mvc application i have created custom model binder and IModelBinderProvider
to handle currency when currency is posted to server in thousands format, like $123,456.00
I want invoke custom model binder only if model's property has certain attribute applied to it. Below is my code
public interface IScrubberAttribute
{
object Scrub(string modelValue, out bool success);
}
public class CurrencyScrubberAttribute : Attribute, IScrubberAttribute
{
public object Scrub(string modelValue, out bool success)
{
// do something
}
}
public class ScrubbingModelBinder : DefaultModelBinder
{
IScrubberAttribute _attribute;
public ScrubbingModelBinder(Type type, IScrubberAttribute attribute)
{
_attribute = attribute as IScrubberAttribute;
}
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// do something
}
}
public class ScrubbingModelBinderProvider : IModelBinderProvider
{
public IModelBinder GetBinder(Type modelType)
{
if (modelType == typeof(decimal) || modelType == typeof(decimal?))
{
//??? ISSUE: the line below always returns null.
var attribute = modelType.GetCustomAttributes(typeof(CurrencyScrubberAttribute), false).FirstOrDefault();
if (attribute != null)
{
return new ScrubbingModelBinder(modelType, attribute as IScrubberAttribute);
}
}
return null;
}
}
My Model
public class MyModel
{
[CurrencyScrubber]
public decimal? MyValue { get; set; }
}
I have register ScrubbingModelBinderProvider
in application startup so it is getting invoked.
Issue
In ScrubbingModelBinderProvider
i am trying to find if the property has [CurrencyScrubber]
attribute applied; and if it is then only invoke ScrubbingModelBinder
.
However modelType.GetCustomAttributes()
method cannot find or return CurrancyScrubber
attribute. When i do quick watch in debug mode, the modelType.GetCustomAttributes()
method returns 3 attributes but none is of type CurrancyScrubber
Upvotes: 0
Views: 1854
Reputation: 34089
The IModelBinderProvider
approach i posted in original question was used in asp.net core application and it works in asp.net core. I was trying to use the same approach in classic asp.net. However in classic asp.net you cannot get custom attribute. Related SO post here and here.
So to solve this issue i override BindProperty
method of System.Web.Mvc.DefaultModelBinder
where i can get custom attribute
public class ScrubbingModelBinder : DefaultModelBinder
{
public ScrubbingModelBinder()
{
}
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
{
var propBindAttr = propertyDescriptor.Attributes.OfType<CurrencyScrubberAttribute>().FirstOrDefault();
if (propBindAttr != null &&
(propertyDescriptor.PropertyType == typeof(decimal) || propertyDescriptor.PropertyType == typeof(decimal?)))
{
var value = bindingContext.ValueProvider.GetValue(propertyDescriptor.Name);
if (value != null && value.AttemptedValue != null)
{
var success = true;
var result = propBindAttr.Scrub(value.AttemptedValue, out success);
if (success)
{
propertyDescriptor.SetValue(bindingContext.Model, result);
return;
}
}
}
base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
}
}
and then register it in Application_Start()
method as DefaultBinder. There is no need to use IModelBinderProvider
.
ModelBinders.Binders.DefaultBinder = new ScrubbingModelBinder();
so now default binder will be used for all the properties as before, except for the one that has [CurrencyScrubber]
attribute.
Upvotes: 1