web-dev-qa-db-fra.com

Le moyen le plus propre de mapper l'entité au DTO avec Linq Select?

J'ai essayé de trouver un moyen propre et réutilisable de mapper des entités à leurs DTO. Voici un exemple de ce que j'ai trouvé et où je suis coincé.

Entités

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
}

DTO

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

C'est ainsi que j'ai commencé à gérer le mappage. Je voulais une solution qui n'exécuterait pas la requête avant le mappage. On m'a dit que si vous passez un Func<in, out> au lieu de Expression<Func<in, out>> qu'il exécutera la requête avant le mappage.

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 problème avec ceci est que j'ai déjà une expression qui mappe un Address à un AddressDTO donc j'ai du code dupliqué. Cela se cassera également si person.Address est nul. Cela devient très rapide, surtout si je veux afficher d'autres entités liées à la personne dans ce même DTO. Il devient un nid d'oiseaux de cartographies imbriquées.

J'ai essayé ce qui suit, mais Linq ne sait pas comment le gérer.

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

Existe-t-il des solutions élégantes qui me manquent?

15
Jeff

Utilisez simplement AutoMapper .

Exemple:

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

Votre requête s'exécutera lorsque le mappage sera effectué, mais s'il existe des champs dans l'entité qui ne vous intéressent pas, utilisez Project().To<> qui est disponible à la fois pour NHibernate et EntityFramework. Il fera effectivement une sélection sur les champs spécifiés dans les configurations de mappage.

8
Christian

Si vous souhaitez créer des mappages manuellement, vous pouvez utiliser Select sur la collection de la manière suivante:

Quelques données de 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"}}
    };

Méthodes de cartographie:

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

Utilisation réelle:

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

Gardez à l'esprit que si c'était une vraie requête, elle ne serait pas exécutée tant qu'elle était IQueryable, elle serait exécutée une fois que vous la matérialiseriez (en utilisant ToList () par exemple).

Cependant, j'envisagerais d'utiliser un cadre qui pourrait le faire (les mappages) pour vous automatiquement (si vos mappages sont aussi simples que l'exemple fourni (.

8
Andrew B

Vous pouvez soit utiliser AutoMapper ou écrire des méthodes d'extension comme celles-ci:

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

Vous pouvez ensuite mapper un objet Person à un objet PersonDTO comme ceci:

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);
    }
}
1
chomba