I've created custom TypeDescriptionProvider for one of my MVC models. I use it for dynamic assignment of ValidationAttribute.
I use value of one property to decide what attributes to add to other properties. In web service, where I use DataAnnotationsValidationRunner, validation works fine.
Source of runner: here
internal static class DataAnnotationsValidationRunner
{
public static IEnumerable<ErrorInfo> GetErrors(object instance)
{
return from prop in TypeDescriptor.GetProperties(instance).Cast<PropertyDescriptor>()
from attribute in prop.Attributes.OfType<ValidationAttribute>()
where !attribute.IsValid(prop.GetValue(instance))
select new ErrorInfo(prop.Name, attribute.FormatErrorMessage(string.Empty), instance);
}
}
To get property value I use following code (in MyCustomTypeDescriptor)
public override PropertyDescriptorCollection GetProperties()
{
var originalProperties = base.GetProperties();
var newProperties = new List<PropertyDescriptor>();
var myProperty = originalProperties.Find("CountryCodeID", false)
var myId = (int)countryProperty.GetValue(base.GetPropertyOwner(myProperty));
foreach (PropertyDescriptor pd in originalProperties)
{
AttributeCollection runtimeAttributes = pd.Attributes;
// add new attributes based on myId value
....
}
return new PropertyDescriptorCollection(newProperties.ToArray());
}
When using this model with this descriptor in MVC View, I get following exception:
Value cannot be null. Parameter name: primary Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.ArgumentNullException: Value cannot be null. Parameter name: primary
What is the correct way of getting value of property within TypeDescriptor? I use this descriptor through provider on model type, not instance (e.g. global.asax).
EDIT: I've found workaround. In GetTypeDescriptor method of MyTypeDescriptorProvider I use instance parameter and pass it to consctructor of MyCustomTypeDescriptor. However, MVC validation doesn't wok. I though it uses these dynamic data automatically (similar to runner mentioned above).
EDIT 2: Using workaroud I almost always see instance null. So it is not possible to get value there and put it to consctructor of TypeDescriptor...
Thank you!
Finally I was able to use customTypeDescriptor for both - generation of HTML tags needed for client validation and validating model during binding.
First MyCustomTypeDescriptor.cs:
Then to bind this descriptor (per INSTANCE!) I use MyModelValidatorProvider:
This works fine, however, during ModelBinding, no ViewData is set, so ValidatorProvider doesn't hook. As a solution to this I used MyModelBinder:
Now I can use MyCustomTypeDescriptor with DataAnnotationRunner to validate all MVC web, MVC other classes than controllers, html helpers (unobtrusive validation) and in other projects such as WCF services...
All this is fine, however it just doesn't feel right. It would be great if I was able to somehow hook MyCustomTypeDescriptor directly to MVC, however as this link claims, it doesn't seem to be possible.
How can I provide my own ICustomTypeDescriptor in ASP.NET MVC?
Any improvements that could help make this solution more elegant are welcome. Thank you.