Sorin Vasiliu
Sorin Vasiliu

Reputation: 217

Where to place mappings using Automapper static initialization API

I am using Automapper 6.2.2 and I'm trying to set it up in a Web App. I am trying to use the static Automapper.Initialize() method placed directly in my Global.asax file.

public class WebApiApplication : HttpApplication
{
    protected void Application_Start()
    {

        AutoMapper.Mapper.Initialize(cfg =>
        {
            cfg.AllowNullCollections = true;

            cfg.CreateMap<LoadArea, LoadAreaWithoutPlannedSlotDto>();
            cfg.CreateMap<LoadArea, LoadAreaDto>();
            cfg.CreateMap<LoadAreaForCreationDto, LoadArea>().Ignore(d => d.Slots);
            cfg.CreateMap<LoadArea, LoadAreaForUpdateDto>();
            cfg.CreateMap<LoadAreaForUpdateDto, LoadArea>().ForMember(m => m.Code, i => i.UseDestinationValue());

            cfg.CreateMap<PlannedSlot, PlannedSlotDto>();
            cfg.CreateMap<PlannedSlotForCreationDto, PlannedSlot>().Ignore(d => d.Area);
            cfg.CreateMap<PlannedSlotForUpdateDto, PlannedSlot>();
            cfg.CreateMap<UserToReturnDto, User>();
            cfg.CreateMap<LoadAreaSlotDetailForReturnDto, LoadAreaSlotDetail>();

        });

        AreaRegistration.RegisterAllAreas();
        UnityConfig.RegisterComponents();
        GlobalConfiguration.Configure(WebApiConfig.Register);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

    }
}

The strange issue is that while this code runs at startup, the mappings are created but none of them are actually configured.

So if I try to Ignore a property in the Mapper.Initialize(...) method, it doesn't work and I get an error when the unmapped property is run into when mapping occurs.

I tried using:

cfg.CreateMap<LoadAreaSlotDetailForReturnDto, LoadAreaSlotDetail>().ForMember(d => d.LoadArea, opt => opt.Ignore());

Also tried:

cfg.CreateMap<LoadAreaSlotDetailForReturnDto, LoadAreaSlotDetail>(MemberList.None);

And a few other combinations, including an extension method that would ignore all unmapped members:

        public static IMappingExpression<TSource, TDestination> Ignore<TSource, TDestination>(this IMappingExpression<TSource, TDestination> map,
    Expression<Func<TDestination, object>> selector)
    {
        map.ForMember(selector, config => config.Ignore());
        return map;
    }

But what does work is if I try to Ignore the property Inline in my controller as follows:

[HttpPost]
    [Route("{loadAreaId}/details")]
    public IHttpActionResult AddLoadAreaSlotDetails([FromUri] string loadAreaId, [FromBody] LoadAreaSlotDetailForAddDto loadAreaSlotDetails)
    {

        var loadAreaSlotDetailEntity = Mapper.Map<LoadAreaSlotDetailForAddDto, LoadAreaSlotDetail>(loadAreaSlotDetails, opt => opt.ConfigureMap().ForMember(d => d.LoadArea, o => o.Ignore()));
        _repo.AddLoadAreaSlotDetail(loadAreaSlotDetailEntity);

        return Ok();
    }

This proves to me that the Ignore works but at the same time I assume that I'm Initializing and configuring my mappings wrongly but I don't know why because many other examples are Initializing in the same way using the static API. I'm doing the same in a .NET Core project (in the ConfigureServices method) and mappings work, it also ignores unmapped properties by default.

Why does this happen ?

Upvotes: 1

Views: 1425

Answers (1)

Emma Middlebrook
Emma Middlebrook

Reputation: 876

Have you tried using AutoMapper Profiles?

AutoMapper Configuration

I was then able to configure this in the Startup.cs of my WebApi application. I was using SimpleInjector as my Container:

var profiles =
    Assembly
         .GetExecutingAssembly()
         .GetTypes()
         .Where(t => typeof(Profile).IsAssignableFrom(t))
         .ToList();

Mapper.Initialize(
    mp =>
        {
            var mapperConfiguration = new MapperConfiguration(cfg => cfg.AddProfiles(profiles));

            var mapper = mapperConfiguration.CreateMapper();

            container.Register(() => mapper, Lifestyle.Scoped);
        });

You then need to define one or more profiles depending on how you want to split out your auto mapper config.

public class UserProfile : Profile
{
    public UserProfile()
    {
        CreateMap<UserDetails, UserTransferObject>();
        CreateMap<UserAndAccountDetails, UserAndAccountTransferObject>();
        CreateMap<User, UserAndAccountTransferObject>()
            .ForMember(
                dest => dest.DifferentPropertyName,
                orig => orig.MapFrom(src => src.OriginalPropertyName));     
    }
}

Upvotes: 3

Related Questions