简体   繁体   English

我可以在同一个字段上多次使用IMetadataAware属性吗?

[英]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. 如果我尝试使用不同的属性 - DisplayForUserTypeDisplayForUserType2DisplayForUserType3 ,一切都按预期工作。

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.

相关问题 自定义验证属性在同一字段上多次 - Custom Validation Attribute Multiple Times on same field 我可以在同一个查询中多次使用参数吗? - Can i use a parameter multiple times in the same query? MySQL IN子句:当我多次使用相同的值时,如何获得多行? - MySQL IN clause: How can I get multiple rows when I use a same value multiple times? 我可以在应用程序中多次使用相同的SQL连接字符串吗? - Can I use same SQL connection string multiple times in my application? 我可以多次将同名的 DataTable 添加到 DataSet 中吗? - can I add the same DataTable with same name to a DataSet multiple times? 如果我在不同时间使用相同Linq查询的不同字段,Entity Framework是否会多次查询数据库? - Does Entity Framework query the database multiple times if I use different fields of the same Linq query at different times? CommandLineParser - 多次使用相同的开关/标志 - CommandLineParser - Use the same switch/flag multiple times 我可以在同一个Stream上使用多个BinaryWriters吗? - Can I use multiple BinaryWriters on the same Stream? 可以在泛型类声明中使用相同的泛型类型MULTIPLE TIMES吗? - Can you use the same generic type MULTIPLE TIMES in a generic class declaration? 如何使用同一方法多次刷新父组件中的子组件? - How can I refresh a child component from a parent component multiple times from the same method?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM