Cleanest Way To Map Entity To DTO With Linq Select?
Solution 1
Just use AutoMapper.
Example:
Mapper.CreateMap<Address, AddressDTO>();
Mapper.CreateMap<Person, PersonDTO>();
Your query will execute when the mapping is performed but if there are fields in the entity that you're not interested use
Project().To<>
which is available both for NHibernate and EntityFramework. It will effectively do a select on the fields specified in the mapping configurations.
Solution 2
If you want to create mappings manually then you can use Select on the collection in the following way:
Some test data:
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"}}
};
Mapping methods:
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
};
}
Actual usage:
var personsDTO = persons.Select(x => ToPersonDTOMap(x)).ToList();
Keep in mind that if this was a real query is would not get executed as long as it was IQueryable, it would be executed once you materialize it (using ToList() for example).
However, I would consider using some framework which could do it (the mappings) for you automatically (if your mapping are as simple as provided example(.
Solution 3
You could either use AutoMapper or write extension methods like these:
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());
}
}
You could then map a Person
object to a PersonDTO
object like this:
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);
}
}
Solution 4
Automapper is the best way .
For me, I use this for simple objects only, but I don't recommend it
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;
}
}
And I call it like that wherever I want
var myDTO= ObjectMapper.Map(MyObject, new MyObjectDTO());
Bacon
Updated on July 27, 2021Comments
-
Bacon almost 3 years
I've been trying to come up with a clean and reusable way to map entities to their DTOs. Here is an example of what I've come up with and where I'm stuck.
Entities
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; } }
Expressions
This is how I began to handle the mapping. I wanted a solution that wouldn't execute the query before mapping. I've been told that if you pass a
Func<in, out>
instead ofExpression<Func<in, out>>
that it will execute the query before mapping.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 } } }
One issue with this is that I already have an expression that maps an
Address
to anAddressDTO
so I have duplicated code. This will also break ifperson.Address
is null. This gets messy very quick especially if I want to display other entities related to person in this same DTO. It becomes a birds nest of nested mappings.I've tried the following but Linq doesn't know how to handle it.
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 } } }
Are there any elegant solutions that I'm missing?