Asp.net-Mvc-3

使用 AutoMapper 將元數據傳輸到視圖模型的技術

  • March 3, 2017

我使用 AutoMapper 將我的域對象映射到我的視圖模型。我的域層中有元數據,我想將其轉移到視圖層和 ModelMetadata。(此元數據不是 UI 邏輯,而是為我的視圖提供必要的資訊)。

現在,我的解決方案是使用單獨的 MetadataProvider(獨立於 ASP.NET MVC),並使用約定通過 AssociatedMetadataProvider 將相關元數據應用於 ModelMetadata 對象。這種方法的問題在於,在綁定域中的 ModelMetadata 時,我必須測試與使用 AutoMapping 時相同的約定,而且似乎應該有一種方法可以使這更加正交。誰能推荐一個更好的方法來完成這個?

我使用下面的方法自動將數據註釋從我的實體複製到我的視圖模型。這確保了實體/視圖模型的 StringLength 和 Required 值總是相同的。

它使用 Automapper 配置工作,因此只要 AutoMapper 設置正確,如果視圖模型上的屬性名稱不同,它就可以工作。

您需要創建自定義 ModelValidatorProvider 和自定義 ModelMetadataProvider 才能使其正常工作。我對原因的記憶有點模糊,但我相信伺服器端和客戶端驗證都是如此,以及您基於元數據執行的任何其他格式(例如,必填欄位旁邊的星號)。

注意:我已經稍微簡化了我的程式碼,因為我在下面添加了它,所以可能會有一些小問題。

元數據提供者

public class MetadataProvider : DataAnnotationsModelMetadataProvider
{        
   private IConfigurationProvider _mapper;

   public MetadataProvider(IConfigurationProvider mapper)
   {           
       _mapper = mapper;
   }

   protected override System.Web.Mvc.ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
   {           
       //Grab attributes from the entity columns and copy them to the view model
       var mappedAttributes = _mapper.GetMappedAttributes(containerType, propertyName, attributes);

       return base.CreateMetadata(mappedAttributes, containerType, modelAccessor, modelType, propertyName);

}
}

驗證者提供者

public class ValidatorProvider : DataAnnotationsModelValidatorProvider
{
   private IConfigurationProvider _mapper;

   public ValidatorProvider(IConfigurationProvider mapper) 
   {
       _mapper = mapper;
   }

   protected override System.Collections.Generic.IEnumerable<ModelValidator> GetValidators(System.Web.Mvc.ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes)
   {   
       var mappedAttributes = _mapper.GetMappedAttributes(metadata.ContainerType, metadata.PropertyName, attributes);
       return base.GetValidators(metadata, context, mappedAttributes);
   }
}

上述 2 個類中引用的輔助方法

public static IEnumerable<Attribute> GetMappedAttributes(this IConfigurationProvider mapper, Type sourceType, string propertyName, IEnumerable<Attribute> existingAttributes)
{
   if (sourceType != null)
   {
       foreach (var typeMap in mapper.GetAllTypeMaps().Where(i => i.SourceType == sourceType))
       {
           foreach (var propertyMap in typeMap.GetPropertyMaps())
           {
               if (propertyMap.IsIgnored() || propertyMap.SourceMember == null)
                   continue;

               if (propertyMap.SourceMember.Name == propertyName)
               {
                   foreach (ValidationAttribute attribute in propertyMap.DestinationProperty.GetCustomAttributes(typeof(ValidationAttribute), true))
                   {
                       if (!existingAttributes.Any(i => i.GetType() == attribute.GetType()))
                           yield return attribute;
                   }
               }
           }
       }
   }

   if (existingAttributes != null)
   {
       foreach (var attribute in existingAttributes)
       {
           yield return attribute;
       }
   }

}

其他注意事項

  • 如果您使用依賴注入,請確保您的容器尚未替換內置的元數據提供程序或驗證程序提供程序。在我的例子中,我使用的是 Ninject.MVC3 包,它在創建核心後綁定了其中一個,然後我不得不在之後重新綁定它,以便實際使用我的類。我得到了關於只允許添加一次Required的例外情況,花了一天的大部分時間來追踪它。

引用自:https://stackoverflow.com/questions/9989785