ASP.NET MVC2 ModelMetadataProviders:重写 CreateMetadata() 和 GetMetadataForProperty() 之间有什么区别?

发布于 2024-10-16 09:16:03 字数 522 浏览 0 评论 0原文

我开始探索该框架的扩展点,从 MetadataProviders 开始。我目前已经实现了 使用 RequiredAttribute 成功填充 ModelMetadata.IsRequired 属性,但我似乎找不到两者之间的区别 覆盖 CreateMetadata()GetMetadataForProperty(),因为这两个选项似乎都有效。

一般来说,我见过的示例都会重写CreateMetadata()

  • 使用这两种选项的优点和缺点是什么?
  • 是否存在其中其中一种是首选选项的情况?

另外:是否有任何好的资源(博客、书籍)可以从这个扩展点学习?

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().

  • What are the pro and cons of using either options?
  • Are there any scenarios where one of these are the preferred options?

As an extra: are there any good resources (blogs, books) to learn from this extension point?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

顾忌 2024-10-23 09:16:03

GetMetadataForProperty() 在类 ModelMetadataProvider 上声明。

AssociatedMetadataProvider 派生自 ModelMetadataProviderCreateMetadata()AssociatedMetadataProvider 上声明。您提供的链接中覆盖的 DataAnnotationsMetadataProvider 派生自 AssociatedMetadataProvider

MVC 框架调用 ModelMetadataProviderGetMetadataForProperty() 方法。

重写 CreateMetadata() 对您有用的原因是 AssociatedModelMetadataProviderGetMetadataForProperty() 的默认实现调用了 创建元数据()。它看起来像这样:

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);
}

如果您像在提供的链接中一样对 AssociatedMetadataProvider 进行子类化,那么您首选的扩展点是 CreateMetadata 方法,因为 AssociatedMetadataProvider.GetMetadataForProperty() 方法会预先验证 CreateMetadata() 方法的约定。这样,您就知道,如果您的 CreateMetadata() 方法中存在错误,您就已经知道错误的根源在您的方法中,而不是在传递给它的参数中。

另外,如果您想知道的话,这里是 FilterAttributes() 方法的源代码:

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);
    });
}

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);
    });
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文