I have the following Dto and entity with a nested sub entity.
public class Dto
{
public string Property { get; set; }
public string SubProperty { get; set; }
}
public class Entity
{
public string Property { get; set; }
public SubEntity Sub { get; set; }
}
public class SubEntity
{
public string SubProperty { get; set; }
}
How can I set up a mapping with AutoMapper that will allow me to update an existing instance of Entity with the values from a Dto.
I'm using Mapper.Map(dto, entity)
to update an existing entity but when I try to map Dto.SubProperty
to Entity.Sub.SubProperty
I get an exception for "must resolve to top-level member. Parameter name: lambdaExpression".
If I create a mapping from Dto
to SubEntity
using FromMember
then Entity.Sub
gets replaced with a new instance of SubEntity
but that's not what I want. I just want it to update the properties of the existing instance of SubEntity
on the Sub
property of Entity
.
How can I achieve this?
I solved it by using a combination of the ResolveUsing<T>()
method and implementing IValueResolver
and the ConvertUsing<T>()
method and implementing ITypeConverter<TSource,TDestination>
.
Some of my mapping scenarios are more complicated than normal including bidirectional mapping and nested classes and nested collections. The above helped me to solve them.
As requested, I've included an example solution. This example is much simpler than the actual types I was dealing with.
using System;
using AutoMapper;
namespace TestAutoMapperComplex
{
public class Dto
{
public string Property { get; set; }
public string SubProperty { get; set; }
}
public class Entity
{
public string Property { get; set; }
public SubEntity Sub { get; set; }
}
public class SubEntity
{
public string SubProperty { get; set; }
}
static class MapperConfig
{
public static void Initialize()
{
Mapper.CreateMap<Dto, Entity>()
.ForMember(entity => entity.Sub, memberOptions =>
memberOptions.MapFrom(dto => dto));
Mapper.CreateMap<Dto, SubEntity>();
}
}
static class MapperConfig2
{
private class MyResolver : IValueResolver
{
public ResolutionResult Resolve(ResolutionResult source)
{
var destinationSubEntity = ((Entity)source.Context.DestinationValue).Sub;
Mapper.Map((Dto)source.Value, destinationSubEntity);
return source.New(destinationSubEntity, typeof(SubEntity));
}
}
public static void Initialize()
{
Mapper.CreateMap<Dto, Entity>()
.ForMember(entity => entity.Sub, memberOptions =>
memberOptions.ResolveUsing<MyResolver>());
Mapper.CreateMap<Dto, SubEntity>();
}
}
class Program
{
static void Main(string[] args)
{
MapperConfig.Initialize();
var dto = new Dto {Property = "Hello", SubProperty = "World"};
var subEntity = new SubEntity {SubProperty = "Universe"};
var entity = new Entity {Property = "Good bye", Sub = subEntity};
Mapper.Map(dto, entity);
Console.WriteLine(string.Format("entity.Property == {0}, entity.Sub.SubProperty == {1}",
entity.Property, entity.Sub.SubProperty));
Console.WriteLine(string.Format("entity.Sub == subEntity: {0}",
entity.Sub == subEntity));
}
}
}
If you run the example, which is using MapperConfig
, you'll get the following output:
entity.Property == Hello, entity.Sub.SubProperty == World
entity.Sub == subEntity: False
The string properties all get updated as one would want them to, but entity.Sub
gets replaced with a new instance of SubEntity
which is no good for when you are wanting to update entities for an ORM that will be persisted to a database.
If you modify Main
so that MapperConfig2
is used instead, you'll still have the string properties updated as before, but, entity.sub
still has the same instance of SubEntity
that it had before. Running the example with MapperConfig2
gives this output:
entity.Property == Hello, entity.Sub.SubProperty == World
entity.Sub == subEntity: True
The key difference in MapperConfig2
is that ResolveUsing
is used along with MyResolver
to preserve the value of entity.Sub
.