Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automapper together with Dependency Injection

I currently have the following mapping:

Mapper.CreateMap<Journal, JournalDto>();

Now, Journal contains a member named RefTypeID, which corresponding value exists in another table in the database; to look up this value, I have a service which handles a simple int -> string request. The automapper configuration currently happens in a static class at the start of the program. Is it okay to move the mapping code into a class which gets injected into my DI container or is there a better way?

like image 276
Femaref Avatar asked Nov 17 '10 13:11

Femaref


4 Answers

A better way is to use a customer resolver. Mapping configuration is intended to be static, so custom resolvers are intended to provide mapping for a single member:

Mapper.Initialize(cfg => {
    cfg.ConstructServicesUsing(type => WhateverMefUsesToGetInstances.GetInstance(type));

    cfg.CreateMap<Journal, DisplayJournal>()
        .ForMember(dest => dest.RefTypeName, 
            opt => opt.ResolveUsing<RefTypeNameResolver>());
});

Then your resolver becomes:

[Export(typeof(IRefTypeNameResolver))]
public class RefTypeNameResolver : ValueResolver<Journal, string>, IRefTypeNameResolver
{
    private readonly IRefTypesLookup iRefTypesLookup;

    [ImportingConstructor]
    public RefTypeNameResolver (IRefTypesLookup rtl)
    {
        iRefTypesLookup = rtl;
    }

    protected override string ResolveCore(Journal source)
    {
        return iRefTypesLookup.Lookup(source.RefTypeID);
    }
}

Configuration needs to execute once, which is why the configuration API provides hooks into the execution API (type converters, value resolvers etc.)

like image 141
Jimmy Bogard Avatar answered Oct 12 '22 06:10

Jimmy Bogard


You could take a dependency on IMappingEngine instead of using the static class Mapper.

There's a nice blog post about it here: Mocking out AutoMapper with Dependency Injection

like image 42
Martin R-L Avatar answered Oct 12 '22 07:10

Martin R-L


Here is how I solved it:

I defined an IMappingCreator interface:

public interface IMappingCreator
{
  void CreateMappings();
}

I went ahead and implemented a class with that interface (I'm using MEF as DI container, that's where the attributes are comming from) which is put into the DI container as IMappingCreator:

[Export(typeof(IMappingCreator))]
    public class Mapping : IMappingCreator
    {
        private readonly IRefTypesLookup iRefTypesLookup;


        [ImportingConstructor]
        public Mapping(IRefTypesLookup rtl)
        {
            iRefTypesLookup = rtl;
        }

        public void CreateMappings()
        {
            Mapper.CreateMap<Journal, DisplayJournal>().AfterMap((j, dj) => dj.RefTypeName = iRefTypesLookup.Lookup(j.RefTypeID));
        }
    }

Finally, in my application startup, I fetch all instances of that interface in the container and call the CreateMappings method on them:

    var mappings = container.GetExportedValues<IMappingCreator>();

    foreach (IMappingCreator mc in mappings)
    {
        mc.CreateMappings();
    }

This makes the initial setup quite easy, as all the creation happens in one place, and you can have as many mapping creators as you want (however, you should keep those to a minimum, maybe once per project or so, grabbing all needed services for mapping the specific types in that project).

like image 9
Femaref Avatar answered Oct 12 '22 05:10

Femaref


Here is the newest way of doing it...

https://pintoservice.wordpress.com/2016/01/31/dependency-injection-for-automapper-4-2-in-asp-net-vnext-mvc-project/

Although I personally add the auto-mappings in the controller, not in the repository. This way you can use the same repository for different controllers and have different mappings. Same concept though, just inject the IMapper in the controller instead of the repository.

like image 4
Gene S Avatar answered Oct 12 '22 07:10

Gene S