2011-11-04 62 views
3

當我一直在玩和學習ASP.Net MVC 3時,我一直在使用AutoMapper來映射我的域的實體和我的視圖模型。使用反射來雙向自動映射所有域實體以查看MVC3中的模型

我厭倦了爲實現的每個ViewModel單獨創建地圖。因此,我編寫了一些代碼來掃描我的程序集,並使用一些反射來創建每個必需的映射。但是,因爲我對使用AutoMapper的最佳實踐並不十分熟悉,所以我想我可能會向大家展示我所做的事情,並詢問我的方法是否可能會回來咬我。

基本上我有一個叫AutoMappingConfigurator(在Global.asax.cs中使用)類,如下所示:

public static class AutoMappingConfigurator 
    { 
     public static void Configure(Assembly assembly) 
     { 
      var autoMappingTypePairingList = new List<AutoMappingTypePairing>(); 

      foreach (Type t in assembly.GetTypes()) 
      { 
       var autoMapAttribute = t 
        .GetCustomAttributes(typeof(AutoMapAttribute), true) 
        .OfType<AutoMapAttribute>() 
        .FirstOrDefault(); 

       if (autoMapAttribute != null) 
       { 
        autoMappingTypePairingList 
       .Add(new AutoMappingTypePairing(autoMapAttribute.SourceType, t)); 
       } 
      } 

      autoMappingTypePairingList 
       .ForEach(mappingPair => mappingPair.CreateBidirectionalMap()); 
     } 
    } 

從本質上講它做什麼,是掃描已標有AutoMapAttribute所有類型的組件併爲每個發現它創建一個雙向映射。

AutoMapAttribute是我創建的一個簡單屬性(基於我在線上找到的示例),我附加到ViewModel以指示映射到哪個Domain Entity。

例如。

[AutoMap(typeof(Project))] 
public class ProjectDetailsViewModel 
{ 
    public int ProjectId { get; set; } 
    public string Name { get; set; } 
    public string Description { get; set; } 
} 

關於雙向映射,在我的MVC3工作到目前爲止,我還發現,我經常似乎需要從實體映射到視圖模型的HTTPGET和視圖模型到實體的HttpPost 。

雙向映射爲一個擴展方法實現如下:

public static void CreateBidirectionalMap(this AutoMappingTypePairing mappingPair) 
{ 
    Mapper.CreateMap(mappingPair.SourceType, mappingPair.DestinationType) 
      .IgnoreProperties(mappingPair.DestinationType); 

    Mapper.CreateMap(mappingPair.DestinationType, mappingPair.SourceType) 
      .IgnoreProperties(mappingPair.SourceType); 
} 

關於IgnoreProperties擴展方法,我發現每當我有過,我想忽略的屬性視圖模型(比如當我視圖模型有一個下拉列表,不是基礎域實體的一部分)我似乎不得不通過ForMember AutoMapper方法手動創建忽略。所以我創建了另一個屬性來指示哪些屬性被忽略,所以我在AutoMappingConfigurator中的反射代碼可以自動爲我執行此操作。

的IgnoreProperties擴展方法實現爲一個擴展方法如下:

public static IMappingExpression IgnoreProperties(this IMappingExpression expression 
                , Type mappingType) 
{ 
    var propertiesWithAutoMapIgnoreAttribute = 
     mappingType.GetProperties() 
      .Where(p => p.GetCustomAttributes(typeof(AutoMapIgnoreAttribute), true) 
         .OfType<AutoMapIgnoreAttribute>() 
         .Count() > 0); 
    foreach (var property in propertiesWithAutoMapIgnoreAttribute) 
    { 
     expression.ForMember(property.Name, opt => opt.Ignore()); 
    } 
    return expression; 
} 

這一切都讓我寫我的視圖模型如下並將它AutoMapped:

[AutoMap(typeof(EntityClass))] 
private class ViewModelClass 
{ 
    public int EntityClassId { get; set; } 

    [AutoMapIgnore] 
    public IEnumerable<SelectListItem> DropDownItems { get; set; } 
} 

private class EntityClass 
{ 
    public int EntityClassId { get; set; } 
} 

雖然這工作對我來說,迄今爲止,我擔心由於我對AutoMapper的經驗不足,我可能會反過來咬我。

所以我的問題是:

  • 這是一個很好的方法來設置AutoMapper配置我的映射我的域實體和的ViewModels之間 ?
  • 有沒有關於AutoMapper的東西,我可能會丟失,這將使這種不好的方法?
  • 是連接屬性忽略通過反射和屬性良好 想法?
  • 在我的實體和ViewModel之間創建一個雙向映射是個好主意嗎?
+0

您是否嘗試過使用/查看http://automapper.org/? – MikeSW

+0

是的,我已閱讀所有文檔頁面,並通過各種博客瀏覽,看着無數的計算器問題,我只是沒有看到有人在做我正在做的事情......所以我想知道是否有一個之所以如此...... – mezoid

+0

@mezoid你如何處理自定義映射? – shuniar

回答

1

我看不出有什麼毛病你的方法,併爲您解答:

  • IMO,我相信你設置此功能的方法是創建另有繁瑣的映射關係的好方法你的Models/DTO和你的實體。
  • 我不知道任何關於AutoMapper的東西,這會使這種方法變得很糟糕。
  • 使用屬性來連接屬性忽略是一個好主意,在MVC中屬性被用於這一點。
  • 在大多數情況下,雙向映射聽起來像是一個好主意,但我很好奇這將如何與自定義映射一起工作。

有些事情要考慮:

  1. 如何處理嵌套映射?
  2. 它如何處理自定義映射?
    • 可能屬性?
  3. 雙向映射VS映射每一側帶屬性
    • 哪一個提供更清晰?
    • 哪一個更好地處理自定義/嵌套映射?
  4. 性能是否受到影響?
    • 最有可能不會,但使用反射時可能會擔心。
2

我更喜歡單向視圖模型。換句話說,我向用戶展示數據時使用了一種視圖模型,而當我處理創建時(以及另一種更新等),我使用了另一種視圖模型。

確實,您將通過這種方式獲得更多對象。好處是你可以避免你不需要的視圖模型屬性(因此需要忽略它們)。我認爲視圖模型應該總是儘可能簡單(普通的get/set屬性,如果你有一個需要初始化的列表,也許是一個構造函數)。如果您擔心對象的「公共屬性」的名稱和數據類型,您可以(儘管我認爲您不應該)在接口或基類中定義這些屬性。

如果要在兩個視圖模型中使用特定的域模型屬性,則域模型中的AutoMapIgnore屬性會產生問題。我認爲這也適用於您的解決方案。

最後,我沒有看到使用屬性,而不是像

Mapper.CreateMap<SourceType, DestinationType>(); 

行代碼它可以是任何簡單的多少好處?這可能是一個好主意,雖然有當您從視圖模型映射到模型,忽略了「未映射特性」擴展方法,可以讓你寫

Mapper.CreateMap<SourceType, DestinationType>().IgnoreUnmappedProperties(); 

IMO這是很容易,使用AutoMapIgnore屬性。

0

如果你的模型和實體都有基類,你可以做如下的事情:

 var entityAssembly = typeof(BaseEntity).Assembly; 
     var modelAssembly = typeof(BaseModel).Assembly; 
     var modelNamespace = modelAssembly.GetTypes().Where(a => a.BaseType == typeof(BaseModel)).FirstOrDefault().Namespace; 

     foreach (var entity in entityAssembly.GetTypes().Where(a=> a.BaseType == typeof(BaseEntity))) 
     { 
      var model = modelAssembly.GetType(String.Format("{0}.{1}{2}", modelNamespace, entity.Name, "Model")); 
      if (model != null) 
      { 
       Mapper.CreateMap(entity, model); 
       Mapper.CreateMap(model, entity); 
      } 
     } 

這是一個配置實現的雙向約定,如果相應的模型不存在,則跳到下一個實體。它非常簡單,但是可以替代手動映射。

注意:這使得假設模型和實體名稱遵循某種常規命名。例如。

{實體名稱}模型 eq。分支到分支模型

希望它有幫助。請注意這是一個真正的基本實現。如果沒有模型存在,那麼代碼在第3行發出錯誤。