LP13
LP13

Reputation: 34089

ASP.NET Model binding based on attribute

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

Answers (1)

LP13
LP13

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

Related Questions