It's not so much a question, as I have found a way to do what I want, but it seems like there should be a better way to do it. I've searched everywhere and not found anything.
Basically, I have what I consider a very standard object model.
public class Parent
{
private readonly IList<Child> _children = new List<Child>();
public IEnumerable<Child> Children { get { return _children; } }
public void AddChild(Child child)
{
child.Parent = this;
_children.Add(child);
}
}
and
public class Child
{
public Parent Parent { get; set; }
}
(I've omitted properties irrelevant to the problem and error checking and guarding for the sake of clarity...)
public class ParentDTO
{
List<ChildDTO> Children = new List<ChildDTO>();
}
and
public class ChildDTO
{
}
The reason I'm using a method to add children to the collection is to maintain control of business logic that needs to be processed when a child is added.
With the standard mapping of:
Mapper.CreateMap<Parent, ParentDTO>();
Mapper.CreateMap<ParentDTO, Parent>();
Mapper.CreateMap<Child, ChildDTO>();
Mapper.CreateMap<ChildDTO, Child>();
It seems to work fine coming from the service layer. The children of the domain object map perfectly to the list of ChildDTO
instances.
However, when mapping back the other way, the collection on the domain model isn't set - because it's readonly, obviously. There doesn't appear to be any way to directly set the private field using AutoMapper. I have tried various suggestions found on here, and other parts of the internet.
In the end, I came up with the following mapping:
Mapper.CreateMap<ParentDTO, Parent>()
.ForMember(m => m.Children, o => o.Ignore())
.AfterMap((s, d) =>
{
foreach(var c in s.Children)
d.AddChild(Mapper.Map<ChildDTO, Child>(c));
});
This works as I required. However, I can't help feeling that there has to be a better way, and I haven't tested this with an existing parent that's had children modified and maybe added and removed, so I know it's not actually correct yet. Ultimately, this domain model is persisted using NHibernate, so I do have to worry about that. But, one thing at a time. :)
Hopefully this might help someone else who's encountering the same problem, and maybe someone who's solved it properly will be able to correct me.
Polymorphic element types in collectionsAutoMapper supports polymorphic arrays and collections, such that derived source/destination types are used if found.
If you have to do complex mapping behavior, it might be better to avoid using AutoMapper for that scenario. Reverse mapping can get very complicated very quickly, and unless it's very simple, you can have business logic showing up in mapping configuration.
By default, AutoMapper only recognizes public members. It can map to private setters, but will skip internal/private methods and properties if the entire property is private/internal.
AutoMapper is a great tool when used for simple conversions. When you start using more complex conversions, AutoMapper can be invaluable. For very simple conversions you could of course write your own conversion method, but why write something that somebody already has written?
I don't like to have logic inside property getter or setter, but what about adding setter to Children property of Parent class
public class Parent
{
private readonly IList<Child> _children = new List<Child>();
public IEnumerable<Child> Children
{
get => _children;
set => AddChildren(value);
}
public void AddChild(Child child)
{
child.Parent = this;
_children.Add(child);
}
private void AddChildren(IEnumerable<Child> children)
{
_children.Clear();
foreach (var child in children)
{
AddChild(child);
}
}
}
And AfterMap logic is not more necessary. Mapping configuration will be simple
Mapper.CreateMap<ParentDTO, Parent>();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With