I have a source DTO like this
public class Member
{
public string MemberId {get;set;}
public string MemberType {get;set;}
public string Name {get;set;}
}
The member type can be "Person" or "Company".
And two destination classes like this
public class PersonMember
{
public int PersonMemberId {get;set;}
public string Name {get;set;}
}
public class CompanyMember
{
public int CompanyMemberId {get;set;}
public string Name {get;set;}
}
I want to use Automapper to check what the value of MemberType is in the source class and depending on that type, map to one of the two destination types.
I saw the example of conditionally mapping, but it maps the field it performs the conditional check on. I want to check the condition and map a different field.
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Foo,Bar>()
.ForMember(dest => dest.baz, opt => opt.Condition(src => (src.baz >= 0)));
});
My goal is something like this -
cfg.CreateMap<Member, PersonMember>()
.ForMember(dest => PersonMember.PersonMemberId, opt => if the source.MemberType == "Person" perform mapping from MemberId, otherwise do nothing);
cfg.CreateMap<Member, CompanyMember>()
.ForMember(dest => CompanyMember.CompanyMemberId, opt => if the source.MemberType == "Company" perform mapping from MemberId, otherwise do nothing);
With automapper you must specify return type on invocation mapper eg. mapper.Map<PersonMember>(member)
, this tells that return type is PersonMember
so you can't return CompanyMember
.
You can do something like this:
var configPerson = new MapperConfiguration(cfg => cfg.CreateMap<Member, PersonMember>());
var configCompany = new MapperConfiguration(cfg => cfg.CreateMap<Member, CompanyMember>());
PersonMember personMember = null;
CompanyMember companyMember = null;
switch (member.MemberType )
{
case "PersonMember":
var mapper = configPerson.CreateMapper();
personMember = mapper.Map<PersonMember>(member);
break;
case "CompanyMember":
var mapper = configCompany.CreateMapper();
companyMember = mapper.Map<CompanyMember>(member);
break;
default:
throw new Exception("Unknown type");
break;
}
Or you can try Custom type converters with object
as return type.
Introduce some base class Member
. Inherit PersonMember
, CompanyMember
from the new base class.
Then define these mappings:
cfg.CreateMap<Dto.Member, Member>()
.ConstructUsing((memberDto, context) => {
switch (memberDto.MemberType)
{
case "PersonMember":
return context.Mapper.Map<PersonMember>(memberDto);
case "CompanyMember":
return context.Mapper.Map<CompanyMember>(memberDto);
default:
throw new ArgumentOutOfRangeException($"Unknown MemberType {memberDto.MemberType}");
}
});
cfg.CreateMap<Dto.Member, PersonMember>()
.ForMember(dest => PersonMember.PersonMemberId,
opt => opt.MapFrom(src => src.MemberId));
cfg.CreateMap<Dto.Member, CompanyMember>()
.ForMember(dest => CompanyMember.CompanyMemberId,
opt => opt.MapFrom(src => src.MemberId));
Now you can map using _mapperInstance.Map<Member>(memberDto);
I saw the example of conditionally mapping, but it maps the field it performs the conditional check on. I want to check the condition and map a different field.
Try using such config:
cfg.CreateMap<Member, PersonMember>()
.ForMember(dest => PersonMember.PersonMemberId, opt => {
opt.Condition(src => src.MemberType == "Person");
opt.MapFrom(src => src.MemberId);
});
cfg.CreateMap<Member, CompanyMember>()
.ForMember(dest => CompanyMember.CompanyMemberId, opt => {
opt.Condition(src => src.MemberType == "Company");
opt.MapFrom(src => src.MemberId);
});
In case you mapping from a non-compatible object Id
field will be set to 0
.
For version 5 and above you could try below code:
using System;
using AutoMapper;
namespace AutoMapOneToMulti
{
class Program
{
static void Main(string[] args)
{
RegisterMaps();
var s = new Source { X = 1, Y = 2 };
Console.WriteLine(s);
Console.WriteLine(Mapper.Map<Source, Destination1>(s));
Console.WriteLine(Mapper.Map<Source, Destination2>(s));
Console.ReadLine();
}
static void RegisterMaps()
{
Mapper.Initialize(cfg => cfg.AddProfile<GeneralProfile>());
}
}
public class GeneralProfile : Profile
{
public GeneralProfile()
{
CreateMap<Source, Destination1>();
CreateMap<Source, Destination2>();
}
}
public class Source
{
public int X { get; set; }
public int Y { get; set; }
public override string ToString()
{
return string.Format("Source = X : {0}, Y : {1}", X, Y);
}
}
public class Destination1
{
public int X { get; set; }
public int Y { get; set; }
public override string ToString()
{
return string.Format("Destination1 = X : {0}, Y : {1}", X, Y);
}
}
public class Destination2
{
public int X { get; set; }
public int Y { get; set; }
public override string ToString()
{
return string.Format("Destination2 = X : {0}, Y : {1}", X, Y);
}
}
}
And for version below 5 you could try this:
using System;
using AutoMapper;
namespace AutoMapOneToMulti
{
class Program
{
static void Main(string[] args)
{
RegisterMaps();
var s = new Source { X = 1, Y = 2 };
Console.WriteLine(s);
Console.WriteLine(Mapper.Map<Source, Destination1>(s));
Console.WriteLine(Mapper.Map<Source, Destination2>(s));
Console.ReadLine();
}
static void RegisterMaps()
{
Mapper.Initialize(cfg => cfg.AddProfile<GeneralProfile>());
}
}
public class GeneralProfile : Profile
{
protected override void Configure()
{
CreateMap<Source, Destination1>();
CreateMap<Source, Destination2>();
}
}
public class Source
{
public int X { get; set; }
public int Y { get; set; }
public override string ToString()
{
return string.Format("Source = X : {0}, Y : {1}", X, Y);
}
}
public class Destination1
{
public int X { get; set; }
public int Y { get; set; }
public override string ToString()
{
return string.Format("Destination1 = X : {0}, Y : {1}", X, Y);
}
}
public class Destination2
{
public int X { get; set; }
public int Y { get; set; }
public override string ToString()
{
return string.Format("Destination2 = X : {0}, Y : {1}", X, Y);
}
}
}
If you want a dynamic function, use this extension:
public static dynamic DaynamicMap(this Source source)
{
if (source.X == 1)
return Mapper.Map<Destination1>(source);
return Mapper.Map<Destination2>(source);
}
Console.WriteLine(new Source { X = 1, Y = 2 }.DaynamicMap());
Console.WriteLine(new Source { X = 2, Y = 2 }.DaynamicMap());