Best Practices for Updating Existing Collections?

6,767 views
Skip to first unread message

Jamie Gaines

unread,
Mar 2, 2011, 4:51:32 PM3/2/11
to AutoMapper-users
Does anyone have some pointers on best practices for updating existing
collections?

I have your standard Order-OrderDetail parent-child data structure
that I work with in an MVC 3 application.

To render a page that allows you to edit the order and its details I
pull the proper Order entity out of Entity Framework with it's
associated OrderDetails collection and send it down to the browser.
From there it's transformed into a javascript object that is
manipulated via client-side script. This can of course include
editing existing order details, adding new ones, removing them, etc.

The modified javascript object is then posted back to MVC using the
new JsonValueProviderFactory which transforms it into a DTO version of
the original entities: OrderDto with a collection of OrderDetailDto
objects hanging off it.

The Add Order user story was a dream with AutoMapper. I just mapped
the DTOs to their EF counterparts, added the new entity to the context
and all was good.

It's this Edit Order I'm having trouble understanding. I'd like to
use AutoMapper again to map the updated values from the DTOs to the
entities. But at this point I have a DTO structure with
OrderDetailDto objects that could vary significantly from the
OrderDetail entities that are currently in EF.

I'm confused I should go about removing OrderDetail records from EF
that were removed on the client, adding new ones, etc. AutoMapper
doesn't handle all this for me, correct?

How have you all handled this scenario in your projects?

Jamie Gaines

unread,
Mar 3, 2011, 10:08:24 AM3/3/11
to AutoMapper-users
This Stack Overflow post describes my problem precisely, and a little
better than I did above, if that helps.

http://stackoverflow.com/questions/4587536/design-pattern-for-mapping-dtos-containing-child-collections-back-to-domain-model

It's looking like there's no good way to do this.

Jimmy Bogard

unread,
Mar 8, 2011, 8:18:58 AM3/8/11
to automapp...@googlegroups.com
Question - does the child collection have existing objects in it?  You can try using the "UseDestinationValue" as a start for child objects:

Mapper.CreateMap<OrderDto, Order>().ForMember(dest => dest.Details, opt => opt.UseDestinationValue());

Thanks,

Jimmy

--
You received this message because you are subscribed to the Google Groups "AutoMapper-users" group.
To post to this group, send email to automapp...@googlegroups.com.
To unsubscribe from this group, send email to automapper-use...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/automapper-users?hl=en.


SonOfPirate

unread,
Mar 29, 2011, 2:58:18 PM3/29/11
to AutoMapper-users
I'm in the same boat but need to take it one step farther. I'm using
UseDestinationValue() which worked great and left my child collection
intact, but when I map the DTO into my target object, it adds new
items into the child collection instead of mapping them.

I think this is caused because the instances are different, therefore
Equals will return false unless there is some other way to tell
AutoMapper that they are the same. I've tried overloading Equals in
the target object to return true if the key field(s) are equal but
this hasn't made a difference. Is it possible that I need to
implement Equals in my DTO as the source of the mapping so that
AutoMapper will see that the objects are the same and map the child
DTO into the child item?

matanzg

unread,
Mar 31, 2011, 6:25:23 PM3/31/11
to AutoMapper-users
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.

SonOfPirate

unread,
Apr 2, 2011, 10:45:32 AM4/2/11
to AutoMapper-users
I actually cannot replace the target child collection because there is
a parent-child relationship that must be maintained. In other words,
the child collection accepts a reference to the parent in its
constructor which is then assigned to child objects as they are added
to the collection. Think of it just like the control hierarchy in
ASP.NET.

I am using UseDestinationValue on the collection property in my root
object so that the existing collection will be used instead of
AutoMapper creating a new collection and replacing it. That is
working great. What is not working is that the existing collection
may already contain existing children (loaded from the database) and
want AutoMapper to update those instead of adding new child objects.

Does that help clarify?

matanzg

unread,
Apr 3, 2011, 12:33:41 AM4/3/11
to AutoMapper-users
Let me see if I got you right, the child gets the parent by ctor and
has no setter for it?
The child collection is a lazy loaded HasMany relation?

is it possible that the DTO child collection has:
1. New items?
2. Removed items?
3. Modified items?

Jimmy Bogard

unread,
Apr 15, 2011, 6:48:10 PM4/15/11
to automapp...@googlegroups.com
And this is why I'm loathe to support mapping to existing destination objects - the rules just get crazy.

SonOfPirate

unread,
Apr 16, 2011, 9:02:03 AM4/16/11
to AutoMapper-users
Actually, I'm simply looking for AutoMapper to check if the collection
already contains the target object using Equals and, if so, map the
properties into the existing child object rather than always creating
a new one and adding it into the collection. By default, Equals tests
if the references are equal which will fail and the same behavior as
today will prevail. However, in a case where Equals has been
overridden based on some other criteria, such as an identity property,
then AutoMapper should respect this and proceed to map to this
instance rather than inserting a new one. Make sense?

It's really no different than if I manually coded it this way:

Mapper.CreateMap<SourceType, TargetType>().ForMember(target =>
target.ChildCollection, opt => opt.Ignore());
Mapper.CreateMap<SourceChildType, TargetChildType>();

void DoSomething(SourceType source, TargetType target)
{
// Do some stuff...

Mapper.Map(source, target);

foreach (var child in source.ChildCollection)
{
var targetChild = target.ChildCollection.SingleOrDefault(c =>
c.Equals(child));

if (targetChild == null)
target.ChildCollection.Add(Mapper.Map<SourceChildType,
TargetChildType>(child));
else
Mapper.Map(child, targetChild);
}

// Do some other stuff...
}

This is exactly how I am implementing this in code so it does work.
It would be nice if this behavior was built-into AutoMapper. You
could conditionalize it based on the UseDestinationValue option
otherwise you'll be creating a new collection anyways so the behavior
isn't needed.

If you are looking for a use-case to justify this need, think of the
proliferation of self-tracking entities in .NET development be it from
Linq-to-SQL, Entity Framework or SOA. The need to map values from a
DTO or Domain Object into one of these entities is very common. Often
these entities contain child collections. Think of the classic Order/
OrderLine scenario where I am updating an Order in my UI that has 5
line items by adding two new line items, modifying one and removing
another (by marking it Deleted). When I submit the data back to the
server, I have to first retrieve the existing Order object (and it's
existing OrderLine child objects) then apply the changes so the
persistence layer will recognize the changes and update the data store
accordingly. I cannot simply create seven new OrderLine objects and
add them to the collection. I need to realize that five of the
objects already exist and update them - in this case, three won't
change, one will be marked for deletion and the other updated. I
should only create and add two new OrderLine objects because that's
all that really occurred.

By using Equals, you are putting the responsibility on the developer
to utilize this behavior as needed and keeping the AutoMapper code
pretty clean (imo). Anyway, that's my two cents.

Matan Goldschmiedt

unread,
Apr 17, 2011, 6:10:36 PM4/17/11
to AutoMapper-users
I'd second that requirement - as I write above, we had to manually
clear the collection to apply the new settings, and that only worked
because we had a public method to disconnect the relation and
Nhibernate knows how to handle "fake" dirty entities.

using Equals sounds like a decent solution.
--
--
Make sure to check out my blog at codecovered.net

Jimmy Bogard

unread,
Apr 19, 2011, 9:34:07 PM4/19/11
to automapp...@googlegroups.com
I look forward to the pull request :)

One thing - this should only be used when UseDestinationValue is true.

Matan Goldschmiedt

unread,
Apr 20, 2011, 7:48:56 PM4/20/11
to AutoMapper-users
I'll give it a shot...

On Apr 20, 4:34 am, Jimmy Bogard <jimmy.bog...@gmail.com> wrote:
> I look forward to the pull request :)
>
> One thing - this should only be used when UseDestinationValue is true.
>
Make sure to check out my blog at www.codecovered.net

Matan Goldschmiedt

unread,
Apr 21, 2011, 10:24:40 AM4/21/11
to AutoMapper-users
The only thing that doesn't really work out for me in this solution is
deleted items - If we iterate over the dto's collection and update
those that are equal or add those that are new, we're ignoring those
that were deleted.

I think that items that exist in the destination collection but were
removed in the dto's collection should also be removed from the list,
but then we face the issue of data consistency - removing that item
from the collection doesn't necessarily achieve what we want, for
example an inversed one to many relation won't really remove that item
from the collection if we won't set the reference to null on the many
side of the relation.

Your thoughts?

SonOfPirate

unread,
Apr 22, 2011, 9:29:41 AM4/22/11
to AutoMapper-users
You have to ask yourself how you are communicating 'deleted' items in
the first place. In our case, our objects contain a deleted flag so
the item is never actually removed from the list/collection until it
is persisted - but that is a specific business case.

If you really want full support for updating all changes to a
collection (add, remove, modify), then another approach would be
required. It's not all that difficult and I've implemented similar
behavior in the past in distributed apps that contain collections with
refresh capabilities. In this case, not only would AutoMapper have to
use Equals to test if the DTO should be mapped to an existing
collection item, but then check if the target list contains items that
do not map from the source list and remove them. I think this is the
logic bloat the Jimmy was eluding to.

I'm fine with the simple logic for now where the mapper tests Equals
when UseDestinationValue is used and maps to an existing object rather
than always creating a new one. I can support the delete/remove
behavior using a flag to convey which items are to be removed by my
business logic.

Jimmy Bogard

unread,
Mar 12, 2013, 10:47:33 PM3/12/13
to automapp...@googlegroups.com
Nope, they have not. Mostly because I don't have a failing test :) Get me one and I'll work on it!

On Thursday, March 7, 2013, Sar wrote:
Jimmy,
I am looking for this exact same functionality for mapping collections in AutoMapper . However, it does not seem that the suggestions made in this thread were incorporated into AutoMapper. Please confirm.

Thank you.
Sar
To unsubscribe from this group and stop receiving emails from it, send an email to automapper-use...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

oswald...@gmail.com

unread,
Jun 19, 2015, 10:55:40 AM6/19/15
to automapp...@googlegroups.com
Does that suggestions were implemented already? I'm more one needing that. (From 2011 to 2015)
To unsubscribe from this group and stop receiving emails from it, send an email to automapper-users+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages