I have the below code that has been working in v3 of Automapper but no longer in v5. UPDATE It also works in v4.
CallScheduleProfile
in its constructor sets a Title
property to an instance of a class that passes a value of true
to it.
CallScheduleProfileViewModel
in its constructor sets a Title
property to an instance of a different class that passes a value of true
and "Title"
.
I have setup the mappings in AutoMapper for all 4 classes and then I call Map.
The result is that the after the map the Title
property on CallScheduleProfileViewModel
has a boolean of true
but FriendlyName
is empty even though it's set in its constructor.
What I believe is happening is that the constructor on CallScheduleProfileViewModel
is being called and FriendlyName
is getting assigned but then when the mapping occurs it calls the constructor on Entry
and then maps any properties on UxEntry
that exist and assigns that to the Title
property and by default FriendlyName
will be null and because the FriendlyName
doesn't exist on UxEntry
its value isn't copied across.
I may be wrong in that assumption but either way how do I get FriendlyName
populated on the mapping?
Update : I looked at Automapper documentation on nested types and the issue exists with code supplied in the docs too. If I add a string property to InnerDest
and set its value in OuterDest
constructor, after the Map
its value is null.
public static void Main(string[] args)
{
Mapper.Initialize(cfg =>
{
cfg.CreateMap<UxEntry<bool>, Entry<bool>>();
cfg.CreateMap<CallScheduleProfile, CallScheduleProfileViewModel>();
});
var old = new CallScheduleProfile();
var newmodel = Mapper.Map<CallScheduleProfile, CallScheduleProfileViewModel>(old);
Console.WriteLine(newmodel.Title.Value);
Console.WriteLine(newmodel.Title.FriendlyName);
}
public class UxEntry<T>
{
public static implicit operator T(UxEntry<T> o)
{
return o.Value;
}
public UxEntry()
{
this.Value = default(T);
}
public UxEntry(T value)
{
this.Value = value;
}
public T Value { get; set; }
}
public class CallScheduleProfile
{
public CallScheduleProfile()
{
this.Title = new UxEntry<bool>(true);
}
public UxEntry<bool> Title { get; set; }
}
public class Entry<T>
{
public Entry()
{
}
public Entry(T value, string friendlyName)
{
this.Value = value;
this.FriendlyName = friendlyName;
}
public T Value { get; set; }
public string FriendlyName { get; set; }
public static implicit operator T(Entry<T> o)
{
return o.Value;
}
}
public class CallScheduleProfileViewModel
{
public CallScheduleProfileViewModel()
{
this.Title = new Entry<bool>(true, "Title");
}
public Entry<bool> Title { get; set; }
}
Well, Automapper
maps this property to null
because :
A) Constructor for type Entry<T>
set this property to null
value
B) Automapper creates a new instance of Entry<T>
after (!) the constructor in CallScheduleProfileViewModel
is invoked.
C) There are no other rules set for Automapper
What you can do here is to change configuration, so that you let Automapper know that there should be a default value used for one of the properties :
Mapper.Initialize(cfg =>
{
// when UxEntry is mapped to Entry value "Title" is used for property FriendlyName
cfg.CreateMap<UxEntry<bool>, Entry<bool>>()
.ForMember(dest => dest.FriendlyName, opt => opt.UseValue("Title"));
cfg.CreateMap<CallScheduleProfile, CallScheduleProfileViewModel>();
});
Now we can remove redundant property initialization from constructor in CallScheduleProfileViewModel
.
Running your code without other changes produces following output :
true
Title