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

时间:2015-04-13 19:45:55

标签: c# linq entity-framework linq-to-sql linq-to-entities

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

这个问题的一个问题是我已经有一个将Address映射到AddressDTO的表达式,所以我有重复的代码。如果person.Address为空,这也会中断。如果我想在同一个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
        }
    }
}

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

5 个答案:

答案 0 :(得分:7)

只需使用AutoMapper即可。

示例:

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

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

  

答案 1 :(得分:7)

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

一些测试数据:

    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())。

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

答案 2 :(得分:1)

您可以使用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);
    }
}

答案 3 :(得分:1)

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

我想在任何地方这样称呼

   var myDTO= ObjectMapper.Map(MyObject, new MyObjectDTO());

答案 4 :(得分:0)

我明白你想要的方式。我可以向您推荐这个解决方案:

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

    public static Expression<Func<Entities.Person, PersonDTO>> PersonSelector
    {
        get
        {
            return person => new PersonDTO()
            {
                ID = x.Id,
                Name = x.Name,
                Address = x.Address
                  .Select(AddressDTO.AddressSelector)
            };
        }
    }
}



public async Task<PersonDTO> GetPerson(int id)
{
    var person = await _personRepository.Get(id, PersonDTO.PersonSelector);
    return person;
}

public async Task<TResult> Get<TResult>(int id, Expression<Func<Person, TResult>> selector)
{
    var result = await _context.Persons
        .Where(x => x.Id == id)
        .Select(selector)
        .SingleOrDefaultAsync();

    return result;
}