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?
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.)
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
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).
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.