7

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

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

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”——其他属性永远不会执行。

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

我在这里做错什么了吗?

4

2 回答 2

11

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

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

如果您不覆盖派生属性的 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;
    }
}
于 2014-03-06T15:49:24.167 回答
1

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

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

IMetadataAware从类中删除接口DisplayForUserType

    [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; }
    }
于 2013-03-05T17:48:44.193 回答