2013-03-05 46 views
7

我有不同的人應該用不同的名字看到的字段。我可以在同一個字段多次使用IMetadataAware屬性嗎?

例如,假設我有以下用戶類型:

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只能顯示爲「Address」或「ADR」 - 其他屬性永遠不會執行。

如果我嘗試使用不同的屬性 - DisplayForUserType,DisplayForUserType2,DisplayForUserType3,一切都按預期工作。

我在這裏做錯了什麼嗎?

回答

10

我知道我有點晚這個派對,但我正在尋找相同問題的答案,並無法在網絡上的任何地方找到它。最後我自己解決了這個問題。

簡短的回答是,你可以有多個相同類型的屬性在相同的字段/屬性上實現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; 
    } 
} 
+0

我不知道'TypeId'。如果有人感興趣,這裏有一個很好的解釋:[應該兩個語義相同的屬性的TypeIds是不同的還是相同的?](http://stackoverflow.com/q/8728793/7586)非常感謝, )歡迎來到Stack Overflow! – Kobi 2014-03-06 17:53:51

+0

不用擔心,很高興我可以幫助 - 即使是晚了一年:-) – 2014-03-10 09:47:04

+0

CurrentConte3xt不再可用。是否有可能在MVC5及更高版本中實現類似的功能? – 2015-10-02 09:35:50

1

你的實現是沒有錯的,而是由AssociatedMetadataProvider(以及任何派生類型)元數據創建後應用實現IMetadataAware任何屬性。 要覆蓋默認行爲,您可以實現自定義ModelMetadataProvider

這裏是另一可替換的快速的解決方案:

DisplayForUserType類取下接口IMetadataAware

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

定義一個新的IMetadataAware屬性將由用戶類型應用的顯示邏輯,如下:

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

謝謝!其實,我已經有了一個快速修復 - 我定義了'DisplayForExpertUser','DisplayForNormalUser'和'DisplayForGuestUser' - 這個工作非常好。 – Kobi 2013-03-05 19:12:29

+0

我並不十分理解第一句話:「元數據創建後,實現IMetadataAware的任何屬性都由AssociatedMetadataProvider(以及任何派生類型)應用」 - 如果是這種情況,它應該可以工作。我在這裏看到的行爲看起來像一個錯誤,但我不想妄下結論 - 你是否建議有一個好的解釋? – Kobi 2013-03-05 19:15:06

相關問題