20
votes

For the longest time I've been using AutoMapper to both map my domain models to my DTOs, as well as mapping my DTO back to domain models.

I'm using EF4 for my ORM, and this mapping gets really ugly when the model being mapped contains child collections that need to be add/updated/removed from. As I move forward with my project I keep running into this problem more and more: photos for a blog post, packages for an order, etc.

When going from DTO->domain model, I end up having to add a BeforeMap call that removes all the entities from the domain model's collection and then add a custom ValueResolver for the collection that takes the PK of each entity from the DTO, grabs it from the DB (so that Entity Framework doesn't think I'm adding a new entity), and re-adds it to the domain model's collection and then apply any updates to the individual fields.

This is a really ugly solution, but so are my attempts to manually handle updating these collections. Does anyone have any suggestions for a cleaner approach?

4
Using Automapper to map your domain models probably means your not using Domain Driven Design. Just saying.John Farrell
Is is unrealistic for me to want to handle this mapping cleanly using some auto-magic mapping solution? Should I instead be creating a service purely for updating my domain models from my DTOs?inolen
@jfar how do you figure? First, a domain model isn't synonymous with DDD. Implying that domain models shouldn't be mapped, hand waving this off as "not using Domain Driven Design", and providing no further explanation doesn't seem very helpful, does it? In a layered architecture it's not uncommon to find a service Layer sitting on top of the domain Model. It's also not uncommon for the service layer to talk to the domain, ui and other layers via a tranfer object - helping to keep a "bright line" between these layers.nerraga
Was relieved to find this question... disappointed to not find an answer yet :SDannyT
4 years on, and I'm tackling this same issue, and I'm so sad that there is still no decent answer. In fact the OP's suggestion to use BeforeMap to set the primary keys was the best workaround I've found.David Chiew

4 Answers

3
votes

You may want to use ValueInjecter instead of AutoMapper for this functionality. Check out this question where the makers of both weigh in, AutoMapper vs ValueInjecter . I haven't personally used Value Injecter, but it was built to do what you are trying to do. AutoMapper is better for flattening, but the author of AutoMapper admits that it is not a good tool for "Unflattening", which is what you are trying to do.

1
votes

Because of very bad experience with updating detached object graph I always first load actual object graph from database and manually merge my DTO into this object graph. I usually end up with several helper Merger classes with methods merging DTOs (or view models) to Domain objects. I'm also still looking for better solution.

I also tryed solution where I didn't load object graph from database first. Instead I used custom adapters to build detached domain object graph, attached it to context and set states of all objects and relations. But it was wrong and extremely complex way which couldn't be used for all scenarios (combination of updates and inserts of subentities could not be handled without additional transfered data about initial state).

1
votes

In hibernate there is a cascade option where the updates from to the children..

I think NHibernate has a similar cascadeAll option.

1
votes

If you can relay on the assumption that the DTO's child collection is probably the most updated version, you can actually replace the old collection with the new one.

We had the same issue with NHibernate and we solved it like this:

  1. Use ConstructWith to pull the entity out of the database using the dto's id.
  2. Use BeforeMap to CLEAR the entire child collection (make sure that the reference on the child will be set to null as well).
  3. AutoMapper will automatically copy the new collection.

Luckily, NHibernate was smart enough to apply changes only, I can't tell if EF does the same. This isn't a perfect solution, but it works for a simple domain model.