I've found a lot of information on implementing a custom model binder for validation purposes but I haven't seen much about what I'm attempting to do.
I want to be able to manipulate the values that the model binder is going to set based on attributes on the property in the view model. For instance:
public class FooViewModel : ViewModel
{
[AddBar]
public string Name { get; set; }
}
AddBar is just
public class AddBarAttribute : System.Attribute
{
}
I've not been able to find a clean way to find the attributes on a view model property in the custom model binder's BindModel method. This works but it feels like there should be a simpler solution:
public class FooBarModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = base.BindModel(controllerContext, bindingContext);
var hasBarAttribute = false;
if(bindingContext.ModelMetadata.ContainerType != null)
{
var property = bindingContext.ModelMetadata.ContainerType.GetProperties()
.Where(x => x.Name == bindingContext.ModelMetadata.PropertyName).FirstOrDefault();
hasBarAttribute = property != null && property.GetCustomAttributes(true).Where(x => x.GetType() == typeof(AddBarAttribute)).Count() > 0;
}
if(value.GetType() == typeof(String) && hasBarAttribute)
value = ((string)value) + "Bar";
return value;
}
}
Is there a cleaner way to view the attributes on the view model property or a different kind of attribute I could be using? The DataAnnotation attributes really seem to be for a different problem.
UPDATE
Craig's answer got me to the right place but I thought I'd put some examples in here for others.
The metadata provider I ended up with looks like
public class FooBarModelMetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var metaData = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
if(attributes.OfType<AddBarAttribute>().Any())
metaData.AdditionalValues.Add("AddBarKey", true);
return metaData;
}
}
The model binder looks like:
public class FooBarModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = base.BindModel(controllerContext, bindingContext);
if(bindingContext.ModelMetadata.AdditionalValues.ContainsKey("AddBarKey"))
value = ((string)value) + "Bar";
return value;
}
}
The "correct" way (per the guy who wrote it) is to write a model metadata provider. There's an example at the link. Not precisely "simple," but it works, and you'll be doing what the rest of MVC does.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With