I'm statring to explore the framework's extension points, starting with MetadataProviders. I've currently implemented populating ModelMetadata.IsRequired property using RequiredAttribute
succesfully, but I can't seem to find the difference between
overriding CreateMetadata()
or GetMetadataForProperty()
, since both options seem to work.
In general, the examples I've seen override CreateMetadata()
.
As an extra: are there any good resources (blogs, books) to learn from this extension point?
The GetMetadataForProperty()
is declared on the class ModelMetadataProvider
.
AssociatedMetadataProvider
derives from ModelMetadataProvider
. CreateMetadata()
is declared on AssociatedMetadataProvider
. The DataAnnotationsMetadataProvider
that is overridden in the link you provide is derived from AssociatedMetadataProvider
.
The MVC framework makes calls to ModelMetadataProvider
's GetMetadataForProperty()
method.
The reason overriding CreateMetadata()
is working for you is because the AssociatedModelMetadataProvider
's default implementation of GetMetadataForProperty()
makes a call to CreateMetadata()
. It looks like this:
public override ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, string propertyName)
{
if (containerType == null)
{
throw new ArgumentNullException("containerType");
}
if (string.IsNullOrEmpty(propertyName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "propertyName");
}
PropertyDescriptor propertyDescriptor = this.GetTypeDescriptor(containerType).GetProperties().Find(propertyName, true);
if (propertyDescriptor == null)
{
throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, MvcResources.Common_PropertyNotFound, new object[] { containerType.FullName, propertyName }));
}
return this.GetMetadataForProperty(modelAccessor, containerType, propertyDescriptor);
}
protected virtual ModelMetadata GetMetadataForProperty(Func<object> modelAccessor, Type containerType, PropertyDescriptor propertyDescriptor)
{
IEnumerable<Attribute> attributes = this.FilterAttributes(containerType, propertyDescriptor, propertyDescriptor.Attributes.Cast<Attribute>());
return this.CreateMetadata(attributes, containerType, modelAccessor, propertyDescriptor.PropertyType, propertyDescriptor.Name);
}
If you are subclassing the AssociatedMetadataProvider
as you are in the link you provided, then your preferred extensibility point is the CreateMetadata
method, because the AssociatedMetadataProvider.GetMetadataForProperty()
method pre-validates the contract of your CreateMetadata()
method. That way, you know that if there is an error in your CreateMetadata()
method, you already know that the source of the error is in your method and not in the arguments that were passed to it.
Also, here is the source of the FilterAttributes()
method, in case you were wondering:
protected virtual IEnumerable<Attribute> FilterAttributes(Type containerType, PropertyDescriptor propertyDescriptor, IEnumerable<Attribute> attributes)
{
if (!typeof(ViewPage).IsAssignableFrom(containerType) && !typeof(ViewUserControl).IsAssignableFrom(containerType))
{
return attributes;
}
return attributes.Where<Attribute>(delegate (Attribute a) {
return !(a is ReadOnlyAttribute);
});
}
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