【问题标题】:Can I use an IMetadataAware attribute multiple times on the same field?我可以在同一字段上多次使用 IMetadataAware 属性吗?
【发布时间】:2013-02-19 15:56:31
【问题描述】:

我有不同的人应该以不同的名称看到的字段。

例如,假设我有以下用户类型:

public enum UserType {Expert, Normal, Guest}

我实现了一个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;
    }
}

我的想法是我可以根据需要覆盖其他值,但当我不这样做时,可以使用默认值。例如:

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

问题是当我有多个相同类型的属性时,DataAnnotationsModelMetadataProvider 只为第一个运行OnMetadataCreated
在上面的示例中,Address 只能显示为“地址”或“ADR”——其他属性永远不会执行。

如果我尝试使用不同的属性 - DisplayForUserTypeDisplayForUserType2DisplayForUserType3,一切都会按预期工作。

我在这里做错了吗?

【问题讨论】:

    标签: c# asp.net-mvc-3 data-annotations modelmetadata


    【解决方案1】:

    您的实现没有错,但任何实现IMetadataAware 的属性都在元数据创建后由AssociatedMetadataProvider(和任何派生类型)应用。 要覆盖默认行为,您可以实现自定义ModelMetadataProvider

    这是另一个替代的快速解决方案:

    DisplayForUserType 类中删除接口IMetadataAware

        [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
        public class DisplayForUserTypeAttribute : Attribute//, IMetadataAware
        {
            //your existing code...
        }
    

    定义一个新的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);
                }
            }
        }
    

    模型将是:

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

    【讨论】:

    • 谢谢!实际上,我已经有了一个快速修复方法——我定义了DisplayForExpertUserDisplayForNormalUserDisplayForGuestUser——效果很好。
    • 我不太明白第一句话:“任何实现 IMetadataAware 的属性都在元数据创建后由 AssociatedMetadataProvider(和任何派生类型)应用”——如果是这样的话,它应该有工作。我在这里看到的行为看起来像一个错误,但我不想下结论 - 你是在暗示有一个很好的解释吗?
    【解决方案2】:

    我知道我参加这个聚会有点晚了,但我一直在寻找同一个问题的答案,但在网络上的任何地方都找不到。最后我自己解决了。

    简短的回答是肯定的,您可以拥有多个相同类型的属性,这些属性在同一字段/属性上实现 IMetadataAware 接口。您只需要记住在扩展 Attribute 类时覆盖它的 TypeId 并将其替换为可以为每个派生属性的每个 instance 提供唯一对象的东西。

    如果您不覆盖派生属性的 TypeId 属性,则该类型的所有属性都将被视为相同,因为默认实现将属性的运行时类型作为 id 返回。

    所以现在应该可以按需要进行以下操作:

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

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-12
    • 1970-01-01
    • 2016-03-26
    • 2015-06-11
    • 1970-01-01
    • 2016-12-10
    • 2013-10-30
    相关资源
    最近更新 更多