使用Linq Select将实体映射到DTO的最简洁方法?

Bac*_*con 17 c# linq linq-to-entities entity-framework linq-to-sql

我一直试图想出一种干净且可重复使用的方法来将实体映射到他们的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
}
Run Code Online (Sandbox Code Playgroud)

的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; }
}
Run Code Online (Sandbox Code Playgroud)

表达式

这就是我开始处理映射的方式.我想要一个在映射之前不执行查询的解决方案.我被告知如果你传递一个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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这个问题的一个问题是我已经有一个映射Address到一个表达式,AddressDTO所以我有重复的代码.如果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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我有什么优雅的解决方案吗?

Chr*_*ian 9

只需使用AutoMapper.

例:

Mapper.CreateMap<Address, AddressDTO>();
Mapper.CreateMap<Person, PersonDTO>();
Run Code Online (Sandbox Code Playgroud)

您的查询将在执行映射时执行,但如果您不感兴趣的实体中Project().To<>存在可用于NHibernate和EntityFramework的字段.它将有效地对映射配置中指定的字段进行选择.

  • 可能会增加一些价值:https://rogeralsing.com/2013/12/01/why-mapping-dtos-to-entities-using-automapper-and-entityframework-is-horrible/ (7认同)

And*_*w B 8

如果要手动创建映射,则可以按以下方式对集合使用"选择":

一些测试数据:

    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"}}
    };
Run Code Online (Sandbox Code Playgroud)

映射方法:

    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
        };
    }
Run Code Online (Sandbox Code Playgroud)

实际用途:

var personsDTO = persons.Select(x => ToPersonDTOMap(x)).ToList();
Run Code Online (Sandbox Code Playgroud)

请记住,如果这是一个真正的查询,只要它是IQueryable就不会被执行,它将在你实现它后执行(例如使用ToList()).

但是,我会考虑使用一些可以自动执行它的框架(映射)(如果您的映射与提供的示例一样简单(.

  • 快速评论一下,在最后一个示例中,您不需要箭头函数“var PersonDTO = Persons.Select(ToPersonDTOMap).ToList();” (2认同)

小智 5

Automapper 是最好的方法。

对我来说,我只将其用于简单的对象,但我不推荐它

  public static class ObjectMapper
{
    public static T Map<T>(object objfrom, T objto)
    {
        var ToProperties = objto.GetType().GetProperties();
        var FromProperties = objfrom.GetType().GetProperties();

        ToProperties.ToList().ForEach(o =>
            {
                var fromp = FromProperties.FirstOrDefault(x => x.Name == o.Name && x.PropertyType == o.PropertyType);
                if (fromp != null)
                {
                    o.SetValue(objto, fromp.GetValue(objfrom));
                }
            });

        return objto;
    }
}
Run Code Online (Sandbox Code Playgroud)

我随心所欲地这样称呼它

   var myDTO= ObjectMapper.Map(MyObject, new MyObjectDTO());
Run Code Online (Sandbox Code Playgroud)