2013-05-18 48 views
3

我有一個自定義DataAnnotationsModelMetadataProvider,我想返回一個從ModelMetadata派生的對象,這樣我可以在我的剃鬚刀模板中具有額外的屬性。我該如何擴展DataAnnotationsModelMetadata

到目前爲止,我的定義提供只覆蓋了CreateMetadata功能:

protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) 
{ 
    var modelMetadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName); 

    ModelMetadataAttribute mma;    
    foreach (Attribute a in attributes) 
    { 
     mma = a as ModelMetadataAttribute; 
     if (mma != null) 
      mma.Process(modelMetadata);     
    } 

    return modelMetadata; 
}   

使來自ModelMetadataAttribute得出每個屬性都可以做一些自定義操作(實際上只增加了AdditionalValues

但是因爲幾乎所有的我的屬性主要爲我在剃刀模板中生成的html元素添加屬性,我希望視圖中的ModelMetadata包含我想添加的屬性的字典(以及其他一些內容)。

所以,我需要從DataAnnotationsModelMetadata繼承的類。

我不能只調用base.CreateMetadata功能,因爲它不會正確地投給我的派生類。

我想到了製作由base.CreateMetadata函數返回DataAnnotationsModelMetadata公共屬性的副本到我的派生類,但我可以失去信息,這樣好像不安全,不。

我想到了另一種方式是複製/粘貼的CreateMetadata基本代碼,並添加我的邏輯,但似乎醜陋......(我只擁有MVC3源,所以它可能在mvc4已經改變)

也想過從ViewDataDictionnary繼承,這樣我可以提供我的自定義元數據類而不是標準之一,但我沒有一個關於如何做到這一點的線索。 (也承認我對這個問題並沒有太多的瞭解)

我看了很多關於DataAnnotations和提供商的文章,但是找不到類似於我想要做的事情。

那麼,什麼是我選擇這裏?我可以向哪個方向尋找更接近我想要做的事情?

編輯:

我看了看這個問題(很相似):Can I achieve a 'copy constructor' in C# that copies from a derived class? 但它所做的工作屬性的副本,我想避免這種情況。 在這篇文章的最後答案還有一些關於PopulateMetadata,但我無法找到基地提供該功能...

回答

0

經過一番思考,我竟然發現,不需要使用MvcExtensions和副本包含在基類派生一個所有信息的解決方案。

由於DataAnnotationsModelMetadata及其基類除了初始化一些私有或受保護的值之外不做任何事情,因此不存在產生副作用的風險。

public class ArtifyModelMetaDataProvider : DataAnnotationsModelMetadataProvider 
{ 
    private static List<Tuple<FieldInfo, FieldInfo>> _fieldsMap; 

    static ArtifyModelMetaDataProvider() 
    { 
     _fieldsMap = new List<Tuple<FieldInfo, FieldInfo>>(); 
     foreach (FieldInfo customFI in GetAllFields(typeof(ArtifyModelMetadata))) 
      foreach (FieldInfo baseFI in GetAllFields(typeof(DataAnnotationsModelMetadata))) 
       if (customFI.Name == baseFI.Name) 
        _fieldsMap.Add(new Tuple<FieldInfo, FieldInfo>(customFI, baseFI)); 
    } 

    private static List<FieldInfo> GetAllFields(Type t) 
    { 
     List<FieldInfo> res = new List<FieldInfo>(); 

     while (t != null) 
     { 
      foreach (FieldInfo fi in t.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)) 
       if (!fi.IsLiteral) 
        res.Add(fi); 
      t = t.BaseType; 
     } 

     return res; 
    } 

    private static void CopyToCustomMetadata(ModelMetadata baseMetadata, ArtifyModelMetadata customMetadata) 
    { 
     foreach (Tuple<FieldInfo, FieldInfo> t in _fieldsMap) 
      t.Item1.SetValue(customMetadata, t.Item2.GetValue(baseMetadata)); 
    } 

    protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) 
    { 
     ArtifyModelMetadata modelMetadata = new ArtifyModelMetadata(this, containerType, modelAccessor, modelType, propertyName); 
     CopyToCustomMetadata(base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName), modelMetadata); 

     ModelMetadataAttribute mma; 
     Dictionary<string, string> htmlAttributes; 
     object tmp; 
     foreach (Attribute a in attributes) 
     { 
      mma = a as ModelMetadataAttribute; 
      if (mma != null) 
      { 
       mma.Process(modelMetadata); 
       htmlAttributes = mma.GetAdditionnalHtmlAttributes(); 

       if (htmlAttributes != null) 
       { 
        foreach (KeyValuePair<string, string> p in htmlAttributes) 
        { 
         tmp = null; 
         tmp = modelMetadata.AdditionnalHtmlAttributes.TryGetValue(p.Key, out tmp); 
         if (tmp == null) 
          modelMetadata.AdditionnalHtmlAttributes.Add(p.Key, p.Value); 
         else 
          modelMetadata.AdditionnalHtmlAttributes[p.Key] = tmp.ToString() + " " + p.Value; 
        } 
       } 
      } 
      if (mma is TooltipAttribute) 
       modelMetadata.HasToolTip = true; 
     } 

     return modelMetadata; 
    } 
} 

public class ArtifyModelMetadata : DataAnnotationsModelMetadata 
{ 

    public bool HasToolTip { get; internal set; } 

    public Dictionary<string, object> AdditionnalHtmlAttributes { get; private set; } 

    public ArtifyModelMetadata(DataAnnotationsModelMetadataProvider provider, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) 
     : base(provider, containerType, modelAccessor, modelType, propertyName, null) 
    { 
     AdditionnalHtmlAttributes = new Dictionary<string, object>(); 
    } 
} 

如果你想有一個通用的解決方案來獲得基類的字段在派生一個,你不能只用繼承,因爲你在同一種情況我的時候,只需要使用這個類:

public abstract class GenericBaseCopy<Src, Dst> where Dst : Src 
{ 
    private static List<Tuple<FieldInfo, FieldInfo>> _fieldsMap; 

    static GenericBaseCopy() 
    { 
     _fieldsMap = new List<Tuple<FieldInfo, FieldInfo>>(); 
     foreach (FieldInfo customFI in GetAllFields(typeof(Dst))) 
      foreach (FieldInfo baseFI in GetAllFields(typeof(Src))) 
       if (customFI.Name == baseFI.Name) 
        _fieldsMap.Add(new Tuple<FieldInfo, FieldInfo>(customFI, baseFI)); 
    } 

    private static List<FieldInfo> GetAllFields(Type t) 
    { 
     List<FieldInfo> res = new List<FieldInfo>(); 

     while (t != null) 
     { 
      foreach (FieldInfo fi in t.GetFields(BindingFlags.NonPublic | BindingFlags.Instance)) 
       if (!fi.IsLiteral) 
        res.Add(fi); 
      t = t.BaseType; 
     } 

     return res; 
    } 

    public static void Copy(Src baseClassInstance, Dst dstClassInstance) 
    { 
     foreach (Tuple<FieldInfo, FieldInfo> t in _fieldsMap) 
      t.Item1.SetValue(dstClassInstance, t.Item2.GetValue(baseClassInstance)); 
    } 
} 
2

我建議你看一看MvcExtensionshttp://mvcextensions.github.io/

它的一個主要部分正是你正在做的 - 廣泛的模型元數據配置/使用。您可以在那裏找到很多答案,或者直接將其作爲「即用型」解決方案。

+0

+1謝謝!我會深入研究。但是沒有注射的方法可以做同樣的事情嗎? mvcextensions似乎非常強大,但有很多事情我不需要使用。這是否意味着我需要重構所有實際的代碼,以便它使用mvcextension而不是標準的mvc機制? – ppetrov

+0

@ppetrov mvcextensions需要用它的方法擴展你的HttpApplication,但沒有必要。您可以繼續使用DataAnnotations以及extensinos的流暢配置。這是一個開始指南:https://github.com/MvcExtensions/Core/wiki/Getting-started-with-MvcExtensions – Dima

+0

我正在尋找這個鏈接,但我讀得挺快,實際上可能錯過了一些東西。不管怎樣,謝謝!我會等待一天,看看是否有另一種方式做到這一點(沒有mvcextensions或其他第三方庫),但如果沒有,我會去解決您的解決方案,因爲它解決了問題 – ppetrov

相關問題