web-dev-qa-db-ja.com

Linq SelectでエンティティをDTOにマップする最もクリーンな方法は?

私はエンティティをDTOにマップするためのクリーンで再利用可能な方法を考え出そうとしています。ここに私が思いついたものと私が立ち往生しているところの例があります。

エンティティ

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

これが私がマッピングを処理し始めた方法です。マッピングの前にクエリを実行しないソリューションが必要でした。 Func<in, out>の代わりにExpression<Func<in, out>>を渡すと、マッピングの前にクエリが実行されると言われました。

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

これに関する1つの問題は、AddressAddressDTOにマップする式が既にあるため、コードが重複していることです。 person.Addressがnullの場合、これも壊れます。これは、特に同じDTOで人に関連する他のエンティティを表示したい場合に、非常に手間がかかります。ネストされたマッピングの鳥の巣になります。

私は次のことを試しましたが、Linqはそれを処理する方法を知りません。

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

私が見逃しているエレガントなソリューションはありますか?

15
Jeff

AutoMapper を使用するだけです。

例:

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

クエリはマッピングの実行時に実行されますが、エンティティに興味のないフィールドがある場合は、NHibernateとEntityFrameworkの両方で利用可能なProject().To<>を使用します。マッピング構成で指定されたフィールドを効果的に選択します。

8
Christian

マッピングを手動で作成する場合は、次の方法でコレクションで選択を使用できます。

いくつかのテストデータ:

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

マッピング方法:

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

実際の使用法:

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

これが実際のクエリであった場合、IQueryableである限り実行されないことに注意してください(ToList()を使用するなど)。

ただし、自動的にそれを行うことができるフレームワーク(マッピング)を使用することを検討します(マッピングが提供された例のように単純な場合(。

8
Andrew B

AutoMapper を使用するか、次のような拡張メソッドを記述できます。

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

次に、次のようにPersonオブジェクトをPersonDTOオブジェクトにマッピングできます。

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