[英]Can I use an IMetadataAware attribute multiple times on the same field?
I have fields that different people should see in different names. 我有不同的人应该看到不同名称的字段。
For example, suppose I have the following user types: 例如,假设我有以下用户类型:
public enum UserType {Expert, Normal, Guest}
I implemented an IMetadataAware
attribute: 我实现了一个
IMetadataAware
属性:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class DisplayForUserTypeAttribute : Attribute, IMetadataAware
{
private readonly UserType _userType;
public DisplayForUserTypeAttribute(UserType userType)
{
_userType = userType;
}
public string Name { get; set; }
public void OnMetadataCreated(ModelMetadata metadata)
{
if (CurrentContext.UserType != _userType)
return;
metadata.DisplayName = Name;
}
}
The idea is that I can override other values as needed, but fall back on default values when I don't. 我的想法是,我可以根据需要覆盖其他值,但是当我不这样做时,可以使用默认值。 For example:
例如:
public class Model
{
[Display(Name = "Age")]
[DisplayForUserType(UserType.Guest, Name = "Age (in years, round down)")]
public string Age { get; set; }
[Display(Name = "Address")]
[DisplayForUserType(UserType.Expert, Name = "ADR")]
[DisplayForUserType(UserType.Normal, Name = "The Address")]
[DisplayForUserType(UserType.Guest, Name = "This is an Address")]
public string Address { get; set; }
}
The problem is that when I have multiple attributes of the same type, DataAnnotationsModelMetadataProvider
only runs OnMetadataCreated
for the first one. 问题是当我有多个相同类型的属性时,
DataAnnotationsModelMetadataProvider
只为第一个属性运行OnMetadataCreated
。
In the example above, Address
can only be shown as "Address" or "ADR" - the other attributes are never executed. 在上面的示例中,
Address
只能显示为“Address”或“ADR” - 其他属性永远不会执行。
If I try to use different attributes - DisplayForUserType
, DisplayForUserType2
, DisplayForUserType3
, everything is working as expected. 如果我尝试使用不同的属性 -
DisplayForUserType
, DisplayForUserType2
, DisplayForUserType3
,一切都按预期工作。
Am I doing anything wrong here? 我在这里做错了吗?
I know I am bit late to this party but I was looking to the answer to same question and couldn't find it anywhere on the web. 我知道我在这个派对上有点迟了但是我正在寻找同样问题的答案而无法在网上找到它。 In the end I worked it out myself.
最后我自己解决了。
The short answer is yes you can have multiple attributes of the same type that implement IMetadataAware interface on the same field / property. 简而言之,您可以拥有在同一字段/属性上实现IMetadataAware接口的多个相同类型的属性。 You just have to remember to override the TypeId of the Attribute class when extending it and replace it with something that will give you a unique object per instance of each derived attribute.
您只需要记住在扩展它时覆盖Attribute类的TypeId并将其替换为为每个派生属性的每个实例提供唯一对象的东西。
If you don't override the TypeId property of derived attribute then all the attributes of that type are treated the same since the default implementation returns the run time type of the attribute as the id. 如果不覆盖派生属性的TypeId属性,那么该类型的所有属性都将被视为相同,因为默认实现将属性的运行时类型作为id返回。
So the following should now work as desired: 因此,以下内容现在可以按预期工作:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class DisplayForUserTypeAttribute : Attribute, IMetadataAware
{
private readonly UserType _userType;
public DisplayForUserType(UserType userType)
{
_userType = userType;
}
public override object TypeId
{
get
{
return this;
}
}
public string Name { get; set; }
public void OnMetadataCreated(ModelMetadata metadata)
{
if (CurrentContext.UserType != _userType)
return;
metadata.DisplayName = Name;
}
}
You implementation is not wrong, but any attribute that implements IMetadataAware
is applied by the AssociatedMetadataProvider (and any derived type) after the Metadata creation. 您的实现没有错,但实现
IMetadataAware
任何属性都是在创建元数据后由AssociatedMetadataProvider (以及任何派生类型)应用的。 To override the default behavior, you may implement custom ModelMetadataProvider
. 要覆盖默认行为,您可以实现自定义
ModelMetadataProvider
。
Here is another alternate quick solution: 这是另一种替代快速解决方案:
Remove the interface IMetadataAware
from the DisplayForUserType
class. 从
DisplayForUserType
类中删除IMetadataAware
接口。
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class DisplayForUserTypeAttribute : Attribute//, IMetadataAware
{
//your existing code...
}
Define a new IMetadataAware
attribute that will apply the display logic by UserType, as below: 定义一个新的
IMetadataAware
属性,该属性将按UserType应用显示逻辑,如下所示:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class ApplyDisplayForUserTypeAttribute : Attribute, IMetadataAware
{
private readonly string _property;
public ApplyDisplayForUserTypeAttribute(string property)
{
this._property = property;
}
public void OnMetadataCreated(ModelMetadata metadata)
{
var attribues = GetCustomAttributes(metadata.ContainerType
.GetProperty(this._property), typeof(DisplayForUserTypeAttribute))
.OfType<DisplayForUserTypeAttribute>().ToArray();
foreach (var displayForUserTypeAttribute in attribues)
{
displayForUserTypeAttribute.OnMetadataCreated(metadata);
}
}
}
And model will be: 模型将是:
public class Model
{
[Display(Name = "Age")]
[DisplayForUserType(UserType.Guest, Name = "Age (in years, round down)")]
[ApplyDisplayForUserType("Age")]
public string Age { get; set; }
[Display(Name = "Address")]
[DisplayForUserType(UserType.Expert, Name = "ADR Expert")]
[DisplayForUserType(UserType.Normal, Name = "The Address Normal")]
[DisplayForUserType(UserType.Guest, Name = "This is an Address (Guest)")]
[ApplyDisplayForUserType("Address")]
public string Address { get; set; }
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.