2015-06-04 73 views
0

我有我在視圖中使用的域類分離,所以當檢索數據時,我必須將域類映射到視圖類。 直到現在,這已經很簡單了,但現在我有一種情況,我需要將父域子類從域映射到視圖上的父子子類。使用linq來映射父子結構

使用foreach結構工作正常,但我有很多linq方法做域和視圖類之間的映射,需要重構以適應新的需求,如果我知道如何去做會更快與linq。提前致謝。

至於是什麼,我試圖完成見下面的代碼示例:

在庫中我有類:

public class Parent 
{ 
    public int ParentId { get; set; } 
    public string ParentName { get; set; } 
}; 

public class ChildA : Parent 
{ 
    public string ChildPropertyA { get; set; } 
}; 

public class ChildB : Parent 
{ 
    public string ChildPropertyB { get; set; } 
}; 

然後在UI我有類:

public class ParentVM 
{ 
    public int ParentIdVM { get; set; } 
    public string ParentNameVM { get; set; } 
}; 

public class ChildAVM : ParentVM 
{ 
    public string ChildPropertyAVM { get; set; } 
}; 

public class ChildBVM : ParentVM 
{ 
    public string ChildPropertyBVM { get; set; } 
}; 

現在我將有一個服務類,其中的方法將如下所示:

 public GetParentVMs() 
    { 
     var parents = initializeRepositoryClass(); 

     var parentsVM = MapRepositoryToViewClasses(parents); 

     ShowResult(parentsVM); 
    } 

其中:

 public List<Parent> initializeRepositoryClass() 
    { 
     var parents = new List<Parent>(){ 
      new ChildA(){ParentId=1, ParentName="Parent 1", ChildPropertyA="A"}, 
      new Parent(){ParentId=2, ParentName="Parent 2"}, 
      new ChildB(){ParentId=3, ParentName="Parent 3", ChildPropertyB="B"}, 
     }; 
     return parents; 
    } 
    private List<ParentVM> MapRepositoryToViewClasses(List<Parent> parents) 
    { 
     var parentsVM = new List<ParentVM>(); 
     foreach (var item in parents) 
     { 
      if (item is ChildA) 
      { 
       var itemVM = item as ChildA; 
       parentsVM.Add(
        new ChildAVM() { ParentIdVM = itemVM.ParentId, ParentNameVM = itemVM.ParentName, ChildPropertyAVM = itemVM.ChildPropertyA } 
       ); 
      } 
      else if (item is ChildB) 
      { 
       var itemVM = item as ChildB; 
       parentsVM.Add(
       new ChildBVM() { ParentIdVM = itemVM.ParentId, ParentNameVM = itemVM.ParentName, ChildPropertyBVM = itemVM.ChildPropertyB } 
       ); 
      } 
      else 
      { 
       var itemVM = item as Parent; 
       parentsVM.Add(
       new ParentVM() { ParentIdVM = itemVM.ParentId, ParentNameVM = itemVM.ParentName } 
       ); 
      } 
     } 
     return parentsVM; 
    } 
    private void ShowResult(List<ParentVM> parentsVM) 
    { 
     foreach (var item in parentsVM) 
     { 
      if (item is ChildAVM) 
      { 
       var ca = (ChildAVM)item; 
       Console.WriteLine("Child A " + ca.ChildPropertyAVM); 
      } 
      else if (item is ChildBVM) 
      { 
       var cb = (ChildBVM)item; 
       Console.WriteLine("Child B " + cb.ChildPropertyBVM); 
      } 
      else 
      { 
       Console.WriteLine("Parent "); 
      } 
     } 
    } 

上面的代碼將工作,但我想改變的方法MapRepositoryToViewClasses到另一個使用LINQ,和看起來像下面這樣:

 private List<ParentVM> MapRepositoryToViewClassesLinq(List<Parent> parents) 
    { 
     var parentsVM = 
      from p in parents 
       case 
        p is ChildA then select new ChildAVM() {ChildPropertyAVM = p.ChildPropertyA, ...}; 
       else 
        p is ChildB then select new ChildBVM() {ChildPropertyBVM = p.ChildPropertyB, ...}; 
       else 
        select new ParentVM() {ParentIdVM = p.ParentId}; 


    return parentsVM.ToList(); 
    } 

任何想法?謝謝。

回答

1

你需要一些改變你的代碼,使之更好

1)你必須引入一個工廠來創建虛擬機實例。

class VMFactory 
{ 
    public ParentVM Create(Parent obj) 
    { 
     var childA = obj as ChildA; 
     if (childA != null) 
     { 
      return new ChildAVM() { ParentIdVM = childA.ParentId, ParentNameVM = childA.ParentName, ChildPropertyAVM = childA .ChildPropertyA }; 
     } 

     var childB = obj as ChildB; 
     if(childB != null) 
     { 
       return new ChildBVM() { ParentIdVM = childB.ParentId, ParentNameVM = childB.ParentName, ChildPropertyBVM = childB.ChildPropertyB }; 
     } 

     return new ParentVM() { ParentIdVM = obj.ParentId, ParentNameVM = obj.ParentName }; 
    } 
} 

2)現在你可以在MapRepositoryToViewClasses方法簡化代碼

private List<ParentVM> MapRepositoryToViewClasses(List<Parent> parents) 
{ 
    // Factory instance can be provided by the outer scope 
    var factory = new VMFactory(); 
    var parentsVM = new List<ParentVM>(); 
    foreach (var item in parents) 
    { 
     parentsVM.Add(factory.Create(item)); 
    } 

    return parentsVM; 
} 

3)最後一步,讓我們使用LINQ到地圖

private List<ParentVM> MapRepositoryToViewClasses(List<Parent> parents) 
{ 
    // Factory instance can be provided by the outer scope 
    var factory = new VMFactory(); 

    return parents.Select(factory.Create).ToList(); 
} 

它的完成


又一個試圖解決它

1)創建解決常見任務的擴展。

static class Ext 
{ 
    public static ParentVM Map<TIn>(this TIn obj, Func<TIn, ParentVM> func) 
     where TIn : Parent 
    { 
     var source = obj as TIn; 
     return source != null 
      ? func(obj) 
      : null; 
    } 
} 

2)使用擴展方法來獲取虛擬機

private List<ParentVM> MapRepositoryToViewClassesLinq(List<Parent> parents) 
{ 
    var tmp = from p in parents 
       select 
        p.Map<ChildA>(c => new ChildAVM() { ParentIdVM = c.ParentId, ParentNameVM = c.ParentName, ChildPropertyAVM = c.ChildPropertyA }) ?? 
        p.Map<ChildB>(c => new ChildBVM() { ParentIdVM = c.ParentId, ParentNameVM = c.ParentName, ChildPropertyBVM = c.ChildPropertyB }) ?? 
        new ParentVM() { ParentIdVM = obj.ParentId, ParentNameVM = obj.ParentName }; 

    return tmp.ToList(); 
} 
+0

尼斯的答案,但我有許多方法是使用映射李克:從父母中選擇新的父母VM(){...},我想避免一個大的重構。有沒有辦法將新的parentVM(){}替換爲新的factory.create ??。謝謝 – Juan

+0

我真正在尋找的東西,就像我在方法MapRepositoryToViewClassesLinq中顯示的語法。已經映射到我的項目的類使用類似的語法(沒有繼承),我想知道是否有一些方法可以用linq做到這一點,而無需重寫整個方法。 – Juan

+0

要提供一個不需重構的好解決方案並不那麼容易,我會試着展示另一個。 –

1

如果你爲每種類型設置映射擴展,然後映射過程變得微不足道。

SQL小提琴:https://dotnetfiddle.net/a4eQ6S

如何映射(GetParentsVM()

var parents = initializeRepositoryClass(); 
var parentsVM = parents.Map(); 

映射擴展

public static class ParentMappings 
{ 
    public static ChildAVM Map(this ChildA model) 
    { 
     return new ChildAVM() 
      { 
       ParentIdVM = model.ParentId, 
       ParentNameVM = model.ParentName, 
       ChildPropertyAVM = model.ChildPropertyA, 
      }; 
    } 
    public static ChildBVM Map(this ChildB model) 
    { 
     return new ChildBVM() 
      { 
       ParentIdVM = model.ParentId, 
       ParentNameVM = model.ParentName, 
       ChildPropertyBVM = model.ChildPropertyB, 
      }; 
    } 
    public static ParentVM Map(this Parent model) 
    { 
     if (model is ChildA) 
      return ((ChildA)model).Map(); 
     else if (model is ChildB) 
      return ((ChildB)model).Map(); 
     else 
      return new ParentVM() 
       { 
        ParentIdVM = model.ParentId, 
        ParentNameVM = model.ParentName, 
       }; 
    } 
    public static List<ParentVM> Map(this List<Parent> parents) 
    { 
     return parents.Select(p => p.Map()).ToList(); 
    } 
} 
+0

這是一個不錯的方法,但並沒有解決我的問題,在這種情況下,這是爲了避免使用linq完成現有映射的重構。但我可能會在將來使用它。謝謝 – Juan

+1

看看Automapper。如果您匹配的是成員/屬性名稱,則可以使用它自動在對象之間移動。我沒有把它放在答案中,因爲你的屬性名稱不同,所以你必須將屬性名稱映射到某個地方,所以它並沒有真正的幫助。 –