AutoMapper and IoC
Since we’re a big user of IoC containers, namely StructureMap (which was obviously a big inspiration in the design of the configuration), I tried to make AutoMapper IoC-friendly out of the box. It wasn’t friendly at first, Jeffrey Palermo had to prod me…a few times on this one. All of the examples of AutoMapper right now use the static Mapper class like this:
// Configure AutoMapper Mapper.CreateMap<Order, OrderDto>(); // Perform mapping OrderDto dto = Mapper.Map<Order, OrderDto>(order);
But the Mapper class is merely a wrapper around two classes that do the real work – the Configuration class and the MappingEngine class. The Mapper class provides a singleton implementation of Configuration, but you’re not limited to just using Mapper. Instead, we can configure our container to provide everything for us. First, we need to worry about the Configuration class.
###
Configuring…Configuration
AutoMapper configuration, like many other configuration tasks, needs to happen only once per AppDomain, and at the startup of the application. In a current project, we have AutoMapper, StructureMap, NHibernate, and I believe even some log4net configuration executed once at application startup. Depending on the application environment, this could mean the Application_Start event in global.asax for ASP.NET applications, or just Main() for WinForms apps. In any case, we have a BootStrapper class that wraps configuration for all of our tools. This is where we can configure both our container and AutoMapper. Since I’ve only really used StructureMap, we’ll use that as an example.
To configure the Configuration class correctly in our container (alliteration, BOOM), we need to accomplish a few things:
- Make it singleton
- Point requests for IConfiguration to Configuration
- Point requests for IConfigurationProvider to Configuration
- Set up the IObjectMapper[] dependency
The reason for the two interfaces is that AutoMapper separates the operational from the configuration interface in its semantic model. The Configuration class depends on a set of IObjectMappers to do things like configuration validation, and the actual mapping engine will use whatever IObjectMappers we supply. Here’s the Configuration constructor we care about:
public Configuration(IEnumerable<IObjectMapper> mappers) { _mappers = mappers; }
Thanks to a community patch, we can get the default list of mappers from a registry:
public static class MapperRegistry { public static Func<IEnumerable<IObjectMapper>> AllMappers = () => new IObjectMapper[] { new CustomTypeMapMapper(), new TypeMapMapper(), new NewOrDefaultMapper(), new StringMapper(), new FlagsEnumMapper(), new EnumMapper(), new AssignableMapper(), new ArrayMapper(), new DictionaryMapper(), new EnumerableMapper(), new TypeConverterMapper(), new NullableMapper(), }; }
In case we want to supply different mappers, we can simply replace this function with something else. It’s this location where we can provide the list of mappers for our Configuration object. We don’t have to, as our Configuration type only depends on an enumerable collection of mappers, but this is a convenient place to pull from.
Let’s look at our StructureMap configuration for our Configuration object:
public class ConfigurationRegistry : Registry { public ConfigurationRegistry() { ForRequestedType<Configuration>() .CacheBy(InstanceScope.Singleton) .TheDefault.Is.OfConcreteType<Configuration>() .CtorDependency<IEnumerable<IObjectMapper>>().Is(expr => expr.ConstructedBy(MapperRegistry.AllMappers)); ForRequestedType<IConfigurationProvider>() .TheDefault.Is.ConstructedBy(ctx => ctx.GetInstance<Configuration>()); ForRequestedType<IConfiguration>() .TheDefault.Is.ConstructedBy(ctx => ctx.GetInstance<Configuration>()); } }
In our custom StructureMap Registry, we first configure the Configuration object to be singleton, to default to the Configuration type, and configure the constructor dependency to be constructed by our mapper registry. The next two configuration pieces set up the two semantic model interfaces to resolve to the Configuration class. We use the “ConstructedBy” and “GetInstance” to ensure that resolutions for the two interfaces use the configuration we already set up for Configuration. This is so both interfaces resolve to the exact same instance.
Next, we need to focus on the other major piece of AutoMapper – the mapping engine.
Configuring the mapping engine
The AutoMapper mapping engine does all the nitty gritty work of performing a mapping. It is defined by the IMappingEngine interface, and implemented by the MappingEngine class. Our MappingEngine class has a single dependency:
public MappingEngine(IConfigurationProvider configurationProvider) { _configurationProvider = configurationProvider; }
The MappingEngine, unlike our Configuration object, does not need any special caching/lifetime behavior. The MappingEngine is very lightweight, as it’s really a bunch of methods doing interesting things with Configuration. MappingEngine can be singleton if we want, but there’s no need. Configuring the last piece for the IMappingEngine interface is very simple:
ForRequestedType<IMappingEngine>().TheDefaultIsConcreteType<MappingEngine>();
I didn’t feel like scanning the entire assembly, so I just manually set up this pair.
Putting it all together
Finally, let’s show our entire AutoMapper and StructureMap configuration:
[Test] public void Example() { ObjectFactory.Initialize(init => { init.AddRegistry<ConfigurationRegistry>(); }); var configuration1 = ObjectFactory.GetInstance<IConfiguration>(); var configuration2 = ObjectFactory.GetInstance<IConfiguration>(); configuration1.ShouldBeTheSameAs(configuration2); var configurationProvider = ObjectFactory.GetInstance<IConfigurationProvider>(); configurationProvider.ShouldBeTheSameAs(configuration1); var configuration = ObjectFactory.GetInstance<Configuration>(); configuration.ShouldBeTheSameAs(configuration1); configuration1.CreateMap<Source, Destination>(); var engine = ObjectFactory.GetInstance<IMappingEngine>(); var destination = engine.Map<Source, Destination>(new Source {Value = 15}); destination.Value.ShouldEqual(15); }
In this test, I initialize StructureMap with the registry created earlier. Next, I pull various objects from StructureMap, and do some tests to ensure that all configuration types resolve to the exact same instance. Once I have an IConfiguration instance, I can configure AutoMapper as needed.
Once AutoMapper configuration is complete, I can query StructureMap for an IMappingEngine instance, and use it to perform a mapping operation from some source and destination object. Note that I didn’t need to specify the IConfigurationProvider instance for the IMappingEngine resolution, StructureMap wired this dependency up for me.
But this isn’t the only way to do IoC with AutoMapper.
Scenario 2 – Keeping the static class for configuration/lifetime management
If you only want to do IoC for the mapping engine, configuration will be quite a bit shorter. In that scenario, you basically don’t care about the Configuration class. You use the Mapper static class to do all of your configuration, and only configure the IMappingEngine-MappingEngine pair. This is useful if you inject IMappingEngine in places, but not Configuration. We will let Mapper do all of our instance/lifetime management, and simply point our container to Mapper for the mapping engine. In this case, all we need to do is point the MappingEngine’s constructor dependency to the static Mapper class ConfigurationProvider property:
public class MappingEngineRegistry : Registry { public MappingEngineRegistry() { ForRequestedType<IMappingEngine>() .TheDefault.Is.ConstructedBy(() => Mapper.Engine); } }
We do nothing more than point StructureMap to the static Mapper.Engine property for any request of an IMappingEngine type. In our code, we configure everything through the Mapper class:
[Test] public void Example2() { ObjectFactory.Initialize(init => { init.AddRegistry<MappingEngineRegistry>(); }); Mapper.Reset(); Mapper.CreateMap<Source, Destination>(); var engine = ObjectFactory.GetInstance<IMappingEngine>(); var destination = engine.Map<Source, Destination>(new Source {Value = 15}); destination.Value.ShouldEqual(15); }
In practice, classes that need to perform a map will depend on the IMappingEngine, which, at runtime, will resolve through StructureMap simply to the static Mapper.Engine property. In this scenario, we are able to use the Mapper static class directly for configuration, but reserve the ability to depend on an IMappingEngine when needed, where we need to make our dependencies explicit.
Much like other frameworks, the static Mapper class is merely a facade over the Configuration and MappingEngine objects, providing only instance management. If need be, we can use any of our favorite IoC containers to manage this lifetime ourselves, eliminating a dependency on a static class. Whether this is needed or not is up to the end user.