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

39,778

Solution 1

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

Solution 2

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".

Solution 3

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.

Share:
39,778
MHOOS
Author by

MHOOS

Learning new things everyday.

Updated on September 20, 2020

Comments

  • MHOOS
    MHOOS almost 4 years

    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].