Automapper

De Banane Atomic
Aller à la navigationAller à la recherche

Description

Permet le mapping d'un objet de type A vers un objet de type B.
AutoMapper utilise des convention de mapping afin de réduire le paramétrage:

  • Propriétés de même nom
  • Flattening
  • Ignore les références null

Liens

Setup and configuration

Cs.svg
var config = new MapperConfiguration(cfg => {

    // configure mapping from Source to Dest
    cfg.CreateMap<Source, Dest>();

    // load the configuration from AppProfile
    cfg.AddProfile<AppProfile>();

    // load the configuration from all classes who inherits from Profile in the current assembly
    cfg.AddProfiles(typeof(ContainerConfig).Assembly);
});

IMapper mapper = config.CreateMapper();
IMapper mapper = new Mapper(config);

Profile

AProfile.cs
internal sealed class AProfile : Profile
{
    public AProfile()
    {
        this.CreateAToBMapping();
        this.CreateBToAMapping();
    }

    private void CreateAToBMapping()
    {
        this.CreateMap<A, B>()
            .ForMember(dest => dest.B1, opt => opt.MapFrom(src => src.A1));
    }

    private void CreateBToAMapping()
    {
        this.CreateMap<B, A>()
            .ForMember(dest => dest.A1, opt => opt.MapFrom(src => src.B1));;
    }
}

Dependency Injection

Add the nuget package AutoMapper.Extensions.Microsoft.DependencyInjection
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    // add as parameter one type for each assembly
    services.AddAutoMapper(typeof(ProfileTypeFromAssembly1), typeof(ProfileTypeFromAssembly2));

Exemple

Cs.svg
var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<A, B>()
        .ForMember(dst => dst.Q2, opts => opts.MapFrom(src => src.P2)); // map A.P2 → B.Q2
        .ReverseMap(); // map B → A
});
IMapper mapper = config.CreateMapper();

C c = new C() { P1 = 3 };
A a = new A() { P1 = 1, C1 = c };
B b = mapper.Map<B>(a);
// b.P1 = 1, b.Q2 = 2, b.C1P1 = 3

class A
{
    public int P1 { get; set; }
    public int P2 { get; set; }
    public C C1 { get; set; }
}

class B
{
    public int P1 { get; set; }    // mapping par convention avec A.P1
    public int Q2 { get; set; }    // mapping par configuration avec A.P2
    public int C1P1 { get; set; }  // mapping par flattening avec A.C1.P1
}

class C
{
    public int P1 { get; set; }
}
If the mapping has not been configured, you will get an AutoMapper.AutoMapperMappingException

List and polymorphism

Cs.svg
// avec une liste
List<B> listB = Mapper.Map<List<A>, List<B>>(listA);

// polymorphism
cfg.CreateMap<Parent1, Parent2>()
    .Include<Child1, Child2>();
    // IncludeAllDerived(); to include all the derived classes
// without the include, the resulting list contains only Parent2 objects (no Child2 objects)

cfg.CreateMap<Child1, Child2>();

var lp1 = new List<Parent1> { new Parent1 { P1 = 1}, new Child1 { P1 = 2, P2 = 22 } };
var lp2 = mapper.Map<List<Parent2>>(lp1);
// Parent2, Child2

class Parent1
{
    public int P1 { get; set; }
}

class Child1 : Parent1
{
    public int P2 { get; set; }
}

class Parent2
{
    public int P1 { get; set; }
}

class Child2 : Parent2
{
    public int P2 { get; set; }
}

Pass parameter to the mapping

Cs.svg
var config = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<A, B>()
        .ForMember(dest => dest.P1,
            opt => opt.MapFrom((src, dest, destMember, resContext) => src.P1 + (int)resContext.Items["+"]));
});

IMapper mapper = config.CreateMapper();

var a = new A { P1 = 1 };
var b = mapper.Map<B>(a, opt => opt.Items["+"] = 9);

Map 1 object to multiple objects

Cs.svg
var config = new MapperConfiguration(cfg =>
{
    // mappings used in the converter
    cfg.CreateMap<int, FlatD>()
       .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src));
    cfg.CreateMap<D, FlatD>();

    // main mapping using a converter
    cfg.CreateMap<D, IEnumerable<FlatD>>()
       .ConvertUsing<DToFlatDConverter>();
});

IMapper mapper = config.CreateMapper();

var d = new D
{
    Name = "D",
    Ids = { 1, 2, 3 }
};
var flatDs = mapper.Map<IEnumerable<FlatD>>(d).ToList();

// use a converter to flatten D to FlatD
class DToFlatDConverter : ITypeConverter<D, IEnumerable<FlatD>>
{
    public IEnumerable<FlatD> Convert(
        D source,
        IEnumerable<FlatD> destination,
        ResolutionContext context)
    {
        foreach (var id in source.Ids)
        {
            // use a mapping from int to FlatD to fill the id
            var flatD = context.Mapper.Map<FlatD>(id);
            // use a mapping from D to FlatD to fill the Name
            context.Mapper.Map(source, flatD);

            yield return flatD;
        }
    }
}

class D
{
    public string Name { get; set; }
    public ICollection<int> Ids { get; } = new List<int>();
}

class FlatD
{
    public string Name { get; set; }
    public int Id { get; set; }
}

MOQ

Cs.svg
var mapperMock = new Mock<IMapper>();
mapperMock.Setup(x => x.Map<ItemDto>(It.IsAny<Item>()))
          .Returns(new ItemDto());

Installation

NuGet → AutoMapper

ASP.NET Core

NuGet → AutoMapper.Extensions.Microsoft.DependencyInjection

Startup.cs
public void ConfigureServices(IServiceCollection services)
{
    services.AddAutoMapper();
MyController.cs
private readonly IMapper _mapper;

public MyController(IMapper mapper)
{
    _mapper = mapper;

    B b = _mapper.Map<A, B>(a);

Configuration dans Profile