Using AutoMapper.Profile for creating an instance(non-static) mapper
Asked Answered
G

3

15

I use the following method as described in the following answer to create an instance of a mapper:

var platformSpecificRegistry = AutoMapper.Internal.PlatformAdapter.Resolve<IPlatformSpecificMapperRegistry>();
platformSpecificRegistry.Initialize();

var autoMapperCfg = new AutoMapper.ConfigurationStore(new TypeMapFactory(), AutoMapper.Mappers.MapperRegistry.Mappers);
var mappingEngine = new AutoMapper.MappingEngine(_autoMapperCfg);

As described by the following question:

AutoMapper How To Map Object A To Object B Differently Depending On Context.

How would I be able to use[reuse] an Automapper profile class like the following to create an instance of a mapper?

public class ApiTestClassToTestClassMappingProfile : Profile
{
    protected override void Configure()
    {
        base.Configure();
        Mapper.CreateMap<ApiTestClass, TestClass>()
            .IgnoreAllNonExisting();
    }
}

Just to give you an idea on why I require such functionality: I register all Automapper Profile classes into my IoC container [CastleWindsor] using the following method :

    IoC.WindsorContainer.Register(Types.FromThisAssembly()
                                       .BasedOn<Profile>()
                                       .WithServiceBase()
                                       .Configure(c => c.LifeStyle.Is(LifestyleType.Singleton)));

    var profiles = IoC.WindsorContainer.ResolveAll<Profile>();

    foreach (var profile in profiles)
    {
        Mapper.AddProfile(profile);
    }

    IoC.WindsorContainer.Register(Component.For<IMappingEngine>().Instance(Mapper.Engine));

While above completely fulfills the need for initializing my static Mapper class, I really dont have any idea how to re-use my Automapper profile classes for creating instance mappers [using non-static mapper].

Gargan answered 16/3, 2015 at 15:16 Comment(0)
F
23

This is how you create MapperConfiguration with profiles

public static class MappingProfile
{
    public static MapperConfiguration InitializeAutoMapper()
    {
        MapperConfiguration config = new MapperConfiguration(cfg =>
        {
            cfg.AddProfile(new WebMappingProfile());  //mapping between Web and Business layer objects
            cfg.AddProfile(new BLProfile());  // mapping between Business and DB layer objects
        });

        return config;
    }
}

Profiles examples

//Profile number one saved in Web layer
public class WebMappingProfile : Profile
{
    public WebMappingProfile()
    {
        CreateMap<Question, QuestionModel>();
        CreateMap<QuestionModel, Question>();
        /*etc...*/
    }
}

//Profile number two save in Business layer
public class BLProfile: Profile
{
    public BLProfile()
    {
        CreateMap<BLModels.SubModels.Address, DataAccess.Models.EF.Address>();
        /*etc....*/
    }
}

Wire automapper into DI framework (this is Unity)

public static class UnityWebActivator
{
    /// <summary>Integrates Unity when the application starts.</summary>
    public static void Start()
    {
        var container = UnityConfig.GetConfiguredContainer();
        var mapper = MappingProfile.InitializeAutoMapper().CreateMapper();
        container.RegisterInstance<IMapper>(mapper);

        FilterProviders.Providers.Remove(FilterProviders.Providers.OfType<FilterAttributeFilterProvider>().First());
        FilterProviders.Providers.Add(new UnityFilterAttributeFilterProvider(container));

        DependencyResolver.SetResolver(new UnityDependencyResolver(container));

        // TODO: Uncomment if you want to use PerRequestLifetimeManager
        // Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule));
    }

    /// <summary>Disposes the Unity container when the application is shut down.</summary>
    public static void Shutdown()
    {
        var container = UnityConfig.GetConfiguredContainer();
        container.Dispose();
    }
}

Use IMapper in you class for mapping

public class BaseService
{
    protected IMapper _mapper;

    public BaseService(IMapper mapper)
    {
        _mapper = mapper;
    }

    public void AutoMapperDemo(){
        var questions = GetQuestions(token);
        return _mapper.Map<IEnumerable<Question>, IEnumerable<QuestionModel>>(questions);
    }

    public IEnumerable<Question> GetQuestions(token){
        /*logic to get Questions*/
    }
}

My original post can be found here: http://www.codeproject.com/Articles/1129953/ASP-MVC-with-Automapper-Profiles

Fredenburg answered 19/9, 2016 at 10:48 Comment(2)
Is this solution work on Automapper V10 in mvc core applications?Labellum
I've been looking for this information since 1 hour. Thank you.Copter
I
5

You need to make sure your Profile calls the correct CreateMap call:

public class ApiTestClassToTestClassMappingProfile : Profile
{ 
    protected override void Configure()
    {
        CreateMap<ApiTestClass, TestClass>()
            .IgnoreAllNonExisting();
    }
}

CreateMap on the base Profile class associates that map with that profile and configuration.

Also, your IgnoreAllNonExisting should be superseded by MemberList.Source option in the CreateMap call. That says "use the source type as my list of members to verify against instead of the destination type".

Irritable answered 16/3, 2015 at 18:11 Comment(3)
Could you please comment on my existing solution and potential possibility for improvements and performance hit.Gargan
I have no idea. I just know your original Profiles won't allow them to be isolated per mapping engine instance. That's what I was answering.Irritable
I was calling Mapper.CreateMap instead of this.CreateMap so it was configuring using the Mapper singleton instead of the one created by Autofac. This answer helped me out.Tiaratibbetts
G
1

I ended up creating a mapper instance factory as follows:

using AutoMapper;
using AutoMapper.Mappers;
using System.Collections.Generic;

public class MapperFactory<TSource,TDestination> where TSource : new() where TDestination : new()
{
    private readonly ConfigurationStore _config;

    public MapperFactory(IEnumerable<Profile> profiles)
    {
        var platformSpecificRegistry = AutoMapper.Internal.PlatformAdapter.Resolve<IPlatformSpecificMapperRegistry>();

        platformSpecificRegistry.Initialize();

        _config = new AutoMapper.ConfigurationStore(new TypeMapFactory(), AutoMapper.Mappers.MapperRegistry.Mappers);

        foreach (var profile in profiles)
        {
            _config.AddProfile(profile);
        }
    }

    public TDestination Map(TSource sourceItem)
    {
        using (var mappingEngine = new MappingEngine(_config))
        {
            return mappingEngine.Map<TSource, TDestination>(sourceItem);
        }             
    }
}

And now I can have code similar to the following in my solution:

        var profiles = new Profile[]
        {
            new ApiTestClassToTestClassMappingProfile1(),
            new ApiTestClassToTestClassMappingProfile2(),
            new ApiTestClassToTestClassMappingProfile3() 
        };

        var mapper = new MapperFactory<ApiTestClass, TestClass>(profiles);

        var mappedItem = mapper.Map(testClassInstance);

Above code makes maximum re-usability of the profile classes.

Gargan answered 17/3, 2015 at 13:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.