2013-04-20 11 views
22

Moje pytanie brzmi: jaki jest najlepszy sposób, w jaki mogę odwzorować jeden obiekt na inny w najbardziej możliwy do utrzymania sposób. Nie mogę zmienić sposobu, w jaki obiekt DTO, który otrzymujemy, jest bardziej znormalizowany, więc muszę stworzyć sposób na odwzorowanie tego na naszą implementację obiektu.Sprawdzone metody mapowania jednego obiektu na inny

Oto przykładowy kod, aby pokazać, co muszę zdarzyć:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var dto = new Dto(); 

     dto.Items = new object[] { 1.00m, true, "Three" }; 
     dto.ItemsNames = new[] { "One", "Two", "Three" };    

     var model = GetModel(dto); 

     Console.WriteLine("One: {0}", model.One); 
     Console.WriteLine("Two: {0}", model.Two); 
     Console.WriteLine("Three: {0}", model.Three); 
     Console.ReadLine(); 
    } 

    private static Model GetModel(Dto dto) 
    { 
     var result = new Model(); 

     result.One = Convert.ToDecimal(dto.Items[Array.IndexOf(dto.ItemsNames, "One")]); 
     result.Two = Convert.ToBoolean(dto.Items[Array.IndexOf(dto.ItemsNames, "Two")]); 
     result.Three = dto.Items[Array.IndexOf(dto.ItemsNames, "Three")].ToString(); 

     return result; 
    } 
} 

class Dto 
{ 
    public object[] Items { get; set; } 
    public string[] ItemsNames { get; set; } 
} 

class Model 
{ 
    public decimal One { get; set; } 
    public bool Two { get; set; } 
    public string Three { get; set; } 
} 

Myślę, że byłoby świetnie jest, jeśli miałem jakieś klasy odwzorowujący że weźmie w modelu obiektów PropertyInfo, typ I chcę się przekonwertować, a "itemname" chcę wyciągnąć. Czy ktoś ma jakieś sugestie, aby uczynić to czystszym?

Dzięki!

+0

Nie jestem pewien co do mapowania, ale powinno się patrzeć na generycznych i korzystania z kolekcji rodzajowe: http://csharp-station.com/Tutorial/CSharp/Lesson20 – christiandev

+0

Proponuję Costructor modelu, który trwa Dto i mapuje/konwertuje/sprawdza odpowiednio na stałe, ponieważ dostajesz błędy kompilacji, gdy coś zmieni się w dto. Odbicie i tym samym radzenie sobie z łańcuchami nie pomaga w zwiększeniu łatwości konserwacji. – wonko79

Odpowiedz

4

Jest to możliwe wdrożenie generic używając trochę refleksji (pseudo-kodu, nie mają teraz VS):

public class DtoMapper<DtoType> 
{ 
    Dictionary<string,PropertyInfo> properties; 

    public DtoMapper() 
    { 
     // Cache property infos 
     var t = typeof(DtoType); 
     properties = t.GetProperties().ToDictionary(p => p.Name, p => p); 
    } 

    public DtoType Map(Dto dto) 
    { 
     var instance = Activator.CreateInstance(typeOf(DtoType)); 

     foreach(var p in properties) 
     { 
      p.SetProperty(
       instance, 
       Convert.Type(
        p.PropertyType, 
        dto.Items[Array.IndexOf(dto.ItemsNames, p.Name)]); 

      return instance; 
     } 
    } 

Zastosowanie:

var mapper = new DtoMapper<Model>(); 
var modelInstance = mapper.Map(dto); 

To będzie powolny, kiedy utworzyć instancję odwzorowującą, ale znacznie później.

+0

Niestety, potrzeba tutaj nie jest tak prosta, jak by mi się to spodobało, a pozycja Nazwy nie musi być skorelowana z nazwami właściwości na modelu, więc nie sądzę, żeby to działało. – Alex

15

Zdecydowałbym się na AutoMapper, otwartą i bezpłatną bibliotekę odwzorowań, która pozwala mapować jeden typ na inny, w oparciu o konwencje (tj. Mapować publiczne właściwości o tych samych nazwach i tym samym/wyprowadzonym/przekształcalnym typie, a także wiele innych smart ones). Bardzo łatwy w obsłudze, pozwoli Ci osiągnąć coś takiego:

Model model = Mapper.Map<Model>(dto); 

Nie wiesz o specyficznych wymagań, ale AutoMapper obsługuje również custom value resolvers, który powinien Ci pomóc pisząc jedną, ogólną realizację danego elementu odwzorowującego.

+5

Użyliśmy automappera wcześniej, ale mamy sens, że spadł z powodu niskiej wydajności. – Alex

+2

Uzgodnione. Zrobione to samo, skończyło się na większej liczbie robotów spadających automapper, a następnie pisania niestandardowego na pierwszym miejscu. Automapper ma bardzo powolną wydajność – ZolaKt

+1

Mieliśmy prawie taki sam problem z wydajnością, że nie będziemy go używać ponownie. –

1
/// <summary> 
/// map properties 
/// </summary> 
/// <param name="sourceObj"></param> 
/// <param name="targetObj"></param> 
private void MapProp(object sourceObj, object targetObj) 
{ 
    Type T1 = sourceObj.GetType(); 
    Type T2 = targetObj.GetType(); 

    PropertyInfo[] sourceProprties = T1.GetProperties(BindingFlags.Instance | BindingFlags.Public); 
    PropertyInfo[] targetProprties = T2.GetProperties(BindingFlags.Instance | BindingFlags.Public); 

    foreach (var sourceProp in sourceProprties) 
    { 
     object osourceVal = sourceProp.GetValue(sourceObj, null); 
     int entIndex = Array.IndexOf(targetProprties, sourceProp); 
     if (entIndex >= 0) 
     { 
      var targetProp = targetProprties[entIndex]; 
      targetProp.SetValue(targetObj, osourceVal); 
     } 
    } 
} 
+0

Szybki i brudny sposób !! – DKM

Powiązane problemy