Futility: Transforming entities into DTOs

I just posted about how if you’re looking for a solution to a problem that hasn’t already been solved, then you’re probably doing something wrong. Well this is my case of it.

I quite dislike mapping DTOs to entities, it’s a pain, but mostly tedious and tiresome rather than difficult. I decided to try to ease things by creating a library that would resolve entity instances to their DTO counterparts.

My requirements were few but I was determined not to violate any of them.

  1. Refactoring friendly. No strings for property names, changing names should give compiler errors.
  2. Must simplify code.
  3. Must improve maintainability.

First attempt: Explicit mapping

var mapper = new DtoMapper<Customer, CustomerDto>();

mapper.Pair(mapper.Entity.Name, mapper.Dto.CustomerName);
mapper.Pair(mapper.Entity.Address, mapper.Dto.CustomerAddress);

// ... elsewhere ...

CustomerDto dto = mapper.Transform(customer);

With a clever bit of DynamicProxy usage, this implementation successfully mapped properties on an entity to a DTO. I believed it was reasonably clear, but having to use mapper.Entity was a bit obtuse. Dealing with properties on instances without an instance is tricky, especially if you want to avoid using strings.

The explicit mapping is very refactoring friendly. I could rename a property without breaking the mapping, so requirement 1 was satisfied.

var dto = new CustomerDto();

dto.CustomerName = customer.Name;
dto.CustomerAddress = customer.Address;

return dto;

As the above code demonstrates, this implementation isn’t actually any simpler than just doing simple assignments, it’s in-fact more complicated because of the overhead of understanding what the mapper is. This simple assignment method is also refactoring friendly.

So with requirement 2 failed, and requirement 3 no different to doing it manually, it was time to move on.

Second attempt: Implicit mapping

var mapper = new DtoMapper();

mapper.CreateImplicitMap<Customer, CustomerDto>();

// ... elsewhere ...

CustomerDto dto = mapper.Transform(customer);

This is my favorite implementation, it’s clean and smart; however, it’s also useless.

It did the same as the first example, but implicitly mapped any properties that have the same name together. This is fine, but it would ignore anything that don’t have the same names. I could’ve implemented some logic for guessing names, but that would just be asking for trouble.

Tragically this implementation wasn’t that refactoring friendly; you can rename properties, but it would silently stop mapping them unless you renamed it’s partner too. That’s pretty dangerous stuff. Requirement 1 failed.

It does produce less code than the first implementation, and the simple assignment method, so 2 and 3 are covered.

Third attempt: Attributes

public class CustomerDto
{
	[DtoPartner(typeof(Customer), "Name")]
	public string CustomerName { get; set; }

	[DtoPartner(typeof(Customer), "Address")]
	public string CustomerAddress { get; set; }
}

I could live with this implementation, it’s not as clean as the implicit method, but it is still quite clear.

Unfortunately, it fails in the refactoring test. You can’t rename a property on Customer without it breaking the mapping, because the property names are strings. Requirement 1 failed.

You could smooth over this with an inspection unit test, which checks the strings against their types to see if the property exists, but that smells, it’s not a very good library if you have to verify it even works. I could’ve also created a static class to represent the Customer instance properties, but that’s more noise (you’d need 3 classes, instead of just 2); a pre-build step (ala SubSonic) came to mind, but that’s entering into the realm of diminished returns.

Conclusion?

Sometimes the obvious way is the best way. Old fashioned may be old, but that doesn’t make it wrong. Sometimes a cigar is just a cigar.

4 comments ↓

#1 Daniel on 05.14.08 at 1:13 pm

Mappers have always bothered me too, and Iv tried to mess on with reflection, but it never seems flexiable enough. One way to tackle the problem is to use kit like nHibernate so that you have POCOs decoupled from your database schema out of the box, that you can use up and down your application stack.

Good blog, by the way James, give is afew years and you will be a celebrity dev.

#2 James Gregory on 05.14.08 at 1:37 pm

Thanks, that’s very flattering.

This post has kind-of stemmed from using NHibernate. There are some situations where you can’t pass the POCOs around (for example, Web-services) due to them being unserializable. I think you should be using DTOs in these situations anyway, but then you’re back to the problem of mapping between them in an effortless way.

#3 Clément Bouillier on 09.06.08 at 9:42 am

Hi,

I was also bored to write mapping code between object (what we can call Object/object mapping, and not object/relational mapping like NHibernate). But in fact it is very related issues.

I look at “the other side” (i.e. Java), and it exists a framework named Dozer. It makes implicit mapping as you explain, but for more complicated mapping, you make the mapping in XML files like (N)Hibernate does.

I heard you thinking about your three requirements, but unfortunately, I think there is no solution that combine these three requirements, and the same problem exists with ORM.
Personaly, I prefer configuration over code generation (hence I prefer NHibernate or IBatis than generated DAO).
So, on the first requirement, configuration approach fails more or less, because in fact, you will unit test your classes that makes use of the mapper (and not make unit tests for string inspection, that have no value as you said).
On the second requirement, it is the one I prefer (because I am bored to write mapping code, and implicit mapping simplify it. There is a difference between mapping code and configuration complexity because in mapping code, you have to create object instances, to recurse on collections, to handle inheritance mapping (and that code is very boring), whereas with configuration, I just declare mapping between types and fields and I have implicit mapping.
On the third requirement, I think configuration is simpler than code, even if it needs to understand mapping framework. But it is another discussion…

Clément

#4 Lucas Goodwin on 10.02.08 at 12:52 am

Essentially mapping is a form of the serialization problem. As such you can solve it via explicit or implicit serialization.

I prefer implicit (reflection and convention) as the default behaviour with the option of explicit overrides (attributes as I hate programming in XML).

Realistically you should have a unit test that’s checking your serializations in other areas so you should be covering this serialization with a unit test as well. This coverage solves the refactoring requirements.

Leave a Comment