How to inject AutoMapper IMappingEngine with StructureMap
Asked Answered
O

4

15

Most of the examples I've found for Automapper use the static Mapper object for managing type mappings. For my project, I need to inject an IMapperEngine as part of object construction using StructureMap so that we can mock the mapper in unit tests so we can't use the static mapper. I also need to support configuring AutoMapper Profiles.

My question is how can I configure the StructureMap registry so that it can supply an instance of IMappingEngine when an instance of MyService is constructed.

Here is the Service constructor signature:

public MyService(IMappingEngine mapper, IMyRepository myRepository, ILogger logger)

And here is the StructureMap Registry

public class MyRegistry : StructureMap.Configuration.DSL.Registry
{
    public MyRegistry()
    {
        For<IMyRepository>().Use<MyRepository>();
        For<ILogger>().Use<Logger>();
        //what to do for IMappingEngine?
    }
}

And the profile I want to load

public class MyAutoMapperProfile : AutoMapper.Profile
{
    protected override void Configure()
    {
        this.CreateMap<MyModel, MyDTO>();
    }
}
Organo answered 26/9, 2012 at 15:11 Comment(1)
@Sebastian, thanks for editing to prettify the code. Didn't know how to do it.Organo
M
14

The Mapper class has a static property Mapper.Engine. Use this to register the engine with the container:

For<IMappingEngine>().Use(() => Mapper.Engine);

If you need to load your profiles before injecting the engine I would insert that configuration code alongside the above snippet.


Update

Your custom registry would look like this

class MyRegistry : Registry
{
  public MyRegistry()
  {
    For<IMyRepository>().Use<MyRepository>();
    For<ILogger>().Use<Logger>();

    Mapper.AddProfile(new AutoMapperProfile());
    For<IMappingEngine>().Use(() => Mapper.Engine);
  }
}

This code runs once in your bootstrapper and any dependency of type IMappingEngine will afterwards be served with the value of the static property Mapper.Engine which is configured using your custom AutoMapperProfile.

Microbalance answered 26/9, 2012 at 15:34 Comment(1)
Worked for me. Can't believe I lost as much time as I did trying to figure this out.Futurism
M
7

The static API will be removed in version 5.0. Use a MapperConfiguration instance and store statically as needed. Use CreateMapper to create a mapper instance.

in new version (4.2.0 >=) we should hold and pass IMapper through DI.

a simple Configure Service should be like this (ASP.NET Core)

services.AddSingleton<IMapper>(_ => new MapperConfiguration(cfg =>
            {
                cfg.CreateMap<Foo,Bar>();
            })
            .CreateMapper());

and our service layer (with the help of constructor injection) :

public class CrudService<TDocument> : ICrudService<TDocument>
    {
        private readonly IMapper _internalMapper;
        private readonly IRepository<TDocument> _repository;

        public CrudService(IRepository<TDocument> repository, IMapper mapper)                
        {
            _internalMapper = mapper;
            _repository = repository;
        }

        public virtual ServiceResult<string> Create<TModel>(TModel foo)
        {
            var bar = _internalMapper.Map<TDocument>(foo);

            try
            {
                _repository.Create(bar);
            }
            catch (Exception ex)
            {
                return ServiceResult<string>.Exception(ex);
            }

            return ServiceResult<string>.Okay(entity.Id);
        }
}

consider TDocument as Bar, and TModel as Foo


update :
AutoMapper 4.2.1 released – Static is back

After a bit of feedback and soul searching and honestly tired of dealing with questions, some of the static API is restored in this release. You can now (and in the future) use Mapper.Initialize and Mapper.Map

Mailemailed answered 16/2, 2016 at 11:16 Comment(0)
O
1

Here's what I ended up with as I couldn't figure out how to set the configuration on Mapper.Engine and have it passed into For().Use.

public MyRegistry()
{
    For<IMyRepository>().Use<MyRepository>();
    For<ILogger>().Use<Logger>();

    //type mapping
    For<ConfigurationStore>()
        .Singleton()
        .Use(ctx =>
        {
            ITypeMapFactory factory = ctx.GetInstance<ITypeMapFactory>();
            ConfigurationStore store 
                = new ConfigurationStore(factory, MapperRegistry.AllMappers());
            IConfiguration cfg = store;
            cfg.AddProfile<MyAutoMapperProfile>();
            store.AssertConfigurationIsValid();
            return store;
        });
    For<IConfigurationProvider>().Use(ctx => ctx.GetInstance<ConfigurationStore>());
    For<IConfiguration>().Use(ctx => ctx.GetInstance<ConfigurationStore>());
    For<IMappingEngine>().Singleton().Use<MappingEngine>();
    For<ITypeMapFactory>().Use<TypeMapFactory>();
}
Organo answered 26/9, 2012 at 18:10 Comment(6)
What about Mapper.AddProfile()?Microbalance
Sebastian, I'm trying to avoid using the static Mapper object but if I follow your line of thinking would this be the configuration? For<IMappingEngine>() .Use(ctx => { IConfiguration c = Mapper.Configuration; c.AddProfile<MyAutoMapperProfile>(); IMappingEngine e = Mapper.Engine; return e; });Organo
Not quite. This would mean you add your profile whenever a component needs a dependency of type IMappingEngine. Please see the update to my previous answer for the code I had in mind.Microbalance
@JayWalker MappingEngine implements IDisposable so be sure to dispose them!Downtrodden
@MartijnB, where would the dispose take place? I noticed you declared as a singleton.Organo
@JayWalker there a number of options. Declare it as a singleton, so the need for disposing it becomes smaller because you create just 1 instance, so there is less to clean up. In my case, an ASP.NET MVC app, that instance dies when the whole app pool dies, that's good enough for me, particularly because the dispose doesn't have hooks to unmanaged resources. If you use ASP.NET you can use ReleaseAndDisposeAllHttpScopedObjects or you could use a nested container.Downtrodden
D
1

I wrote a blog post that shows my AutoMapper with StructureMap setup. I have created specific registries for AutoMapper 3.1.0 (also works for 3.1.1) and 3.0.0 and 2.2.1.

http://www.martijnburgers.net/post/2013/12/20/My-AutoMapper-setup-for-StructureMap.aspx

Downtrodden answered 6/2, 2014 at 23:11 Comment(1)
I've read your post, but I'm still having trouble. Do you know what I'm doing wrong here #30082045Manikin

© 2022 - 2024 — McMap. All rights reserved.