2015-04-13 4 views
11

Ich habe versucht, eine saubere und wiederverwendbare Möglichkeit zu finden, Entitäten ihren DTOs zuzuordnen. Hier ist ein Beispiel dafür, was ich mir ausgedacht habe und wo ich feststecke.Der sauberste Weg, Entity zu DTO mit Linq Select zu kartieren?

Entities

public class Person 
{ 
    public int ID { get; set; } 
    public string Name { get; set; } 
    public Address Address { get; set; } 
    // Other properties not included in DTO 
} 

public class Address 
{ 
    public int ID { get; set; } 
    public string City { get; set; } 
    // Other properties not included in DTO 
} 

DTOs

public class PersonDTO 
{ 
    public int ID { get; set; } 
    public string Name { get; set; } 
    public AddressDTO Address { get; set; } 
} 

public class AddressDTO 
{ 
    public int ID { get; set; } 
    public string City { get; set; } 
} 

Expressions

Dies ist, wie ich die Zuordnung zu behandeln begann. Ich wollte eine Lösung, die die Abfrage vor dem Mapping nicht ausführen würde. Mir wurde gesagt, dass wenn Sie eine Func<in, out> statt Expression<Func<in, out>> übergeben, dass es die Abfrage vor dem Mapping ausführen wird.

public static Expressions 
{ 
    public static Expression<Func<Person, PersonDTO>> = (person) => new PersonDTO() 
    { 
     ID = person.ID, 
     Name = person.Name, 
     Address = new AddressDTO() 
     { 
      ID = person.Address.ID, 
      City = person.Address.City 
     } 
    } 
} 

Ein Problem dabei ist, dass ich bereits einen Ausdruck haben, die eine Address zu einem Karten AddressDTO so habe ich dupliziert Code. Dies wird auch unterbrochen, wenn person.Address null ist. Das wird sehr schnell unordentlich, besonders wenn ich andere Entitäten anzeigen möchte, die sich auf eine Person in demselben DTO beziehen. Es wird ein Vogelnest von verschachtelten Abbildungen.

Ich habe das folgende versucht, aber Linq weiß nicht, wie man damit umgeht.

public static Expressions 
{ 
    public static Expression<Func<Person, PersonDTO>> = (person) => new PersonDTO() 
    { 
     ID = person.ID, 
     Name = person.Name, 
     Address = Convert(person.Address) 
    } 

    public static AddressDTO Convert(Address source) 
    { 
     if (source == null) return null; 
     return new AddressDTO() 
     { 
      ID = source.ID, 
      City = source.City 
     } 
    } 
} 

Gibt es elegante Lösungen, die ich vermisse?

+0

AutoMapper. Http://automapper.org/ – Christian

+0

I Ich habe AutoMapper schon vorher benutzt, aber ich ging davon aus, dass die Abfrage vor dem Mapping ausgeführt werden musste. Nachdem ich in der Dokumentation nachgesehen habe, sieht es so aus, als hätte es etwas, was ich suche [HIER] (https: // github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions) – Jeff

+1

Ihre Abfrage wird ausgeführt, wenn das Mapping durchgeführt wird, aber wenn es Felder in der Entität gibt, an denen Sie nicht interessiert sind, verwenden Sie 'Project(). To <>' which is verfügbar sowohl für NHibernate als auch für EntityFramework Felder, die in den Mapping-Konfigurationen angegeben sind. – Christian

Antwort

3

Verwenden Sie einfach AutoMapper.

Beispiel:

Mapper.CreateMap<Address, AddressDTO>(); 
Mapper.CreateMap<Person, PersonDTO>(); 

Ihre Anfrage wird ausgeführt, wenn die Zuordnung durchgeführt wird, aber wenn es Felder in der Einheit, die Sie nicht interessiert Gebrauch sind Project().To<>, die sowohl für NHibernate und EntityFramework verfügbar ist . Es wird effektiv eine Auswahl für die in den Mapping-Konfigurationen angegebenen Felder treffen.

+4

Könnte einen Wert hinzufügen: https://rogeralsing.com/2013/12/01/why-mapping-dtos-to-entities-using-automapper-and-entityframework-is-horrible/ – Vaibhav

1

Sie könnte entweder AutoMapper verwenden oder Erweiterungsmethoden wie diese schreiben:

public static class PersonMapper 
{ 
    public static PersonDTO ConvertToDTO(this Person person) 
    { 
     return new PersonDTO { ID = person.ID, Name = person.Name, Address = person.Address.ConvertToDTO() }; 
    } 

    public static IEnumerable<PersonDTO> ConvertToDTO(this IEnumerable<Person> people) 
    { 
     return people.Select(person => person.ConvertToDTO()); 
    } 
} 

public static class AddressMapper 
{ 
    public static AddressDTO ConvertToDTO(this Address address) 
    { 
     return new AddressDTO { ID = address.ID, City = address.City }; 
    } 

    public static IEnumerable<AddressDTO> ConvertToDTO(this IEnumerable<Address> addresses) 
    { 
     return addresses.Select(address => address.ConvertToDTO()); 
    } 
} 

könnten Sie dann Karte ein Person Objekt zu einem PersonDTO Objekt wie folgt:

public class Program 
{ 
    static void Main(string[] args) 
    { 
     Person person = new Person { ID = 1, Name = "John", Address = new Address { ID = 1, City = "New Jersey" } }; 
     PersonDTO personDTO = person.ConvertToDTO(); 
     Console.WriteLine(personDTO.Name); 
    } 
} 
+0

Würde diese Erweiterungsmethoden SELECT verwenden * FROM Persons 'und dann mappt oder würde dies tatsächlich das Ausgabe-SQL so modifizieren, dass nur die Eigenschaften, die in der Erweiterung enthalten sind,' SELECT 'sind. – Jeff

5

Wenn Sie Wenn Sie Mappings manuell erstellen möchten, können Sie Select für die Sammlung wie folgt verwenden:

Einige Daten Test:

var persons = new List<Person> 
    { 
     new Person() {ID = 1, Name = "name1", Address = new Address() {ID = 1, City = "city1"}}, 
     new Person() {ID = 2, Name = "name2", Address = new Address() {ID = 2, City = "city2"}}, 
     new Person() {ID = 3, Name = "name3", Address = new Address() {ID = 1, City = "city1"}} 
    }; 

Mapping Methoden:

public static PersonDTO ToPersonDTOMap(Person person) 
    { 
     return new PersonDTO() 
     { 
      ID = person.ID, 
      Name = person.Name, 
      Address = ToAddressDTOMap(person.Address) 
     }; 
    } 

    public static AddressDTO ToAddressDTOMap(Address address) 
    { 
     return new AddressDTO() 
     { 
      ID = address.ID, 
      City = address.City 
     }; 
    } 

Der tatsächliche Verbrauch:

var personsDTO = persons.Select(x => ToPersonDTOMap(x)).ToList(); 

Denken Sie daran, dass, wenn dies eine echte Abfrage erhalten würde nicht ausgeführt wurde, ist Solange es IQueryable war, würde es ausgeführt werden, sobald Sie es materialisiert haben (zum Beispiel mit ToList()).

Allerdings würde ich mit einigen Rahmen prüfen, welche es tun könnte (die Zuordnungen) automatisch für Sie (wenn Ihr Mapping wie vorgesehen Beispiel so einfach sind (

Verwandte Themen