2015-04-13 7 views
11

Ho cercato di creare un modo pulito e riutilizzabile per mappare le entità ai loro DTO. Ecco un esempio di ciò che ho scoperto e in cui sono bloccato.Il modo più semplice per mappare le entità con DTO con Linq Select?

Enti

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; } 
} 

Espressioni

Ecco come ho iniziato a gestire la mappatura. Volevo una soluzione che non avrebbe eseguito la query prima della mappatura. Mi è stato detto che se si passa uno Func<in, out> anziché Expression<Func<in, out>> che eseguirà la query prima della mappatura.

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 
     } 
    } 
} 

Un problema con questo è che ho già un'espressione che associa un Address a un AddressDTO codice in modo che ho duplicato. Questo si interromperà anche se person.Address è nullo. Questo diventa molto veloce soprattutto se voglio visualizzare altre entità correlate alla persona in questo stesso DTO. Diventa un nido di uccelli di mappature nidificate.

Ho provato quanto segue ma Linq non sa come gestirlo.

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 
     } 
    } 
} 

Ci sono soluzioni eleganti che mi mancano?

+0

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

+0

I Ho usato AutoMapper in precedenza ma ero sotto il presupposto che doveva eseguire la query prima della mappatura. Dopo aver esaminato ulteriormente la documentazione sembra che possa avere qualcosa di simile a quello che sto cercando [QUI] (https: // github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions) – Jeff

+1

La tua query verrà eseguita quando la mappatura viene eseguita ma se ci sono campi nell'entità che non ti interessa usa 'Project(). <>' che è disponibile sia per NHibernate che per EntityFramework. Effettuerà effettivamente un 'select' su campi specificati nelle configurazioni di mappatura. – Christian

risposta

3

Basta usare AutoMapper.

Esempio:

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

Vostri criteri verrà eseguito quando la mappatura è eseguita ma se ci sono campi che non ti interessa l'uso Project().To<> che è disponibile sia per NHibernate e EntityFramework nell'entità . Effettuerà effettivamente una selezione sui campi specificati nelle configurazioni di mappatura.

+4

Potrebbe aggiungere qualche valore: https://rogeralsing.com/2013/12/01/why-mapping-dtos-to-entities-using-automapper-and-entityframework-is-horrible/ – Vaibhav

1

Si potrebbe usare sia AutoMapper o scrivere metodi di estensione come questi:

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()); 
    } 
} 

È quindi possibile mappare un oggetto Person a un oggetto PersonDTO come questo:

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

Utilizzerebbe questi metodi di estensione 'SELECT * FROM Persons' e quindi mappare o questo effettivamente modificherà l'output SQL per solo 'SELECT' quelle proprietà incluse nell'estensione. – Jeff

5

Se vuoi creare manualmente i mapping, quindi puoi utilizzare Seleziona sulla raccolta nel modo seguente:

alcuni dati di prova:

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"}} 
    }; 

metodi di mappatura:

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 
     }; 
    } 

utilizzo effettivo:

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

Tenete a mente che, se questo è stato un vero e proprio query è non sarebbe ottenere eseguito come per quanto fosse IQueryable, sarebbe eseguito una volta materializzato (usando ToList() per esempio).

Tuttavia, vorrei considerare l'utilizzo di un quadro di riferimento che potrebbe farlo (le mappature) in modo automatico (se la mappatura sono così semplice come previsto ad esempio (

Problemi correlati