Normally when you create AutoMapper profile, it is you responsibility to register them. However with the dependency injection container (DI) you have great possibility to automate this.

Sample Application

I will show you how to register AutoMapper profiles with the help of unit tests. For the simplicity: the class CustomerRepository implements just only one method GetAllCustomers, which is just only tested method. This method use the AutoMapper to map collection of objects of type CustomerEntity to collection of objects of type CustomerDomain.

public class CustomerRepository : ICustomerRepository
{
	private readonly IMappingEngine _mappingEngine;
	private readonly IStorage _storage;

	public CustomerRepository(IMappingEngine mappingEngine, IStorage storage)
	{
		if (mappingEngine == null)
		{
			throw new ArgumentNullException("mappingEngine");
		}
		if (storage == null)
		{
			throw new ArgumentNullException("storage");
		}
		_mappingEngine = mappingEngine;
		_storage = storage;
	}

	public IEnumerable<CustomerDomain> GetAllCustomers()
	{
		IEnumerable<CustomerEntity> customerEntities = _storage.GetAllCustomers();
		return _mappingEngine.Map<IEnumerable<CustomerDomain>>(customerEntities);
	}
}
public class CustomerEntity
{
   public int Id { get; set; }
   public string FirstName { get; set; }
   public string Surname { get; set; }
   public string Comment { get; set; }
}
public class CustomerDomain
{
   public int Id { get; set; }
   public string FirstName { get; set; }
   public string Surname { get; set; }
}

The CustomerEntity and CustomerDomain classes are almost the identical, except the CustomerDomain don’t have the property Comment, which is also excluded from the mapping in the AutoMapper profile.

    public class CustomerDomainToCustomerEntityProfile : Profile
    {
        protected override void Configure()
        {
            CreateMap<CustomerDomain, CustomerEntity>()
                .ForMember(customerEntity => customerEntity.Comment, opt => opt.Ignore());
        }
    }

Registration without DI

Typically you would do something like this to register AutoMapper profile.

Mapper.Initialize(mapperConfiguration => mapperConfiguration.AddProfile(new CustomerDomainToCustomerEntityProfile()));

Manually registration is just fine, but for each new profile you must adapt the configuration.

Because with this approach we are initializing AutoMapper once for each application domain, we need use another initialization mechanism to simulate missing and correct configuration in the unit tests.

[TestMethod]
public void GetAllCustomers_ManualMappingConfiguration_AllCustomersAreReturned()
{
	//Arrange
	var configurationStore = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
	configurationStore.AddProfile(new CustomerEntityToCustomerDomainProfile());
	IMappingEngine mappingEngine = new MappingEngine(configurationStore);
	IStorage storage = new MemoryStorage();
	ICustomerRepository customerRepository = new CustomerRepository(mappingEngine, storage);

	Mapper.Initialize(mapperConfiguration => mapperConfiguration.AddProfile(new CustomerDomainToCustomerEntityProfile()));

	//Act
	IEnumerable<CustomerDomain> actualCustomers = customerRepository.GetAllCustomers();

	//Assert
	Mapper.AssertConfigurationIsValid();
	CollectionAssert.AreEqual(GetExpectedCustomers().ToList(), actualCustomers.ToList());
}
private IEnumerable<CustomerDomain> GetExpectedCustomers()
{
	yield return new CustomerDomain
				 {
					 Id = 1,
					 FirstName = "Anton",
					 Surname = "Kalcik"
				 };

	yield return new CustomerDomain
				 {
					 Id = 2,
					 FirstName = "Max",
					 Surname = "Mustermann"
				 };

	yield return new CustomerDomain
				 {
					 Id = 3,
					 FirstName = "Peter",
					 Surname = "Sample"
				 };
}
public class MemoryStorage : IStorage
{
	public IEnumerable<CustomerEntity> GetAllCustomers()
	{

		yield return new CustomerEntity
					 {
						 Id = 1,
						 FirstName = "Anton",
						 Surname = "Kalcik",
						 Comment = "Some Comment"
					 };

		yield return new CustomerEntity
					 {
						 Id = 2,
						 FirstName = "Max",
						 Surname = "Mustermann",
						 Comment = "Some Comment"
					 };

		yield return new CustomerEntity
					 {
						 Id = 3,
						 FirstName = "Peter",
						 Surname = "Sample",
						 Comment = "Some Comment"
					 };
	}
}

As we are creating the new instance of MappingEngine in the each unit test instead of using Mapper.Engine, each unit test is independent from each other. We can simulate the missing configuration, for example.

[TestMethod]
[ExpectedException(typeof(AutoMapperMappingException))]
public void GetAllCustomers_MissingMappingConfiguration_AutoMapperMappingException()
{
	//Arrange
	var configurationStore = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
	IMappingEngine mappingEngine = new MappingEngine(configurationStore);
	IStorage storage = new MemoryStorage();
	ICustomerRepository customerRepository = new CustomerRepository(mappingEngine, storage);

	//Act
	IEnumerable<CustomerDomain> actualCustomers = customerRepository.GetAllCustomers();

	//Assert
	Mapper.AssertConfigurationIsValid();
	CollectionAssert.AreEqual(GetExpectedCustomers().ToList(), actualCustomers.ToList());
}

Registration with the DI

The nice thing about the DI is that you can use assembly scanning to find all AutoMapper profiles, wherever they are.

public static class UnityExtensions
{
	public static void RegisterAutoMapperType(this IUnityContainer container, LifetimeManager lifetimeManager = null)
	{
		RegisterAutoMapperProfiles(container);

		var profiles = container.ResolveAll<Profile>();
		var autoMapperConfigurationStore = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers);
		profiles.Each(autoMapperConfigurationStore.AddProfile);

		autoMapperConfigurationStore.AssertConfigurationIsValid();

		container.RegisterInstance<IConfigurationProvider>(autoMapperConfigurationStore, new ContainerControlledLifetimeManager());
		container.RegisterInstance<IConfiguration>(autoMapperConfigurationStore, new ContainerControlledLifetimeManager());

		container.RegisterType<IMappingEngine, MappingEngine>(lifetimeManager ?? new TransientLifetimeManager(), new InjectionConstructor(typeof(IConfigurationProvider)));
	}

	private static void RegisterAutoMapperProfiles(IUnityContainer container)
	{
		IEnumerable<Type> autoMapperProfileTypes = AllClasses.FromAssemblies(AppDomain.CurrentDomain.GetAssemblies())
					   .Where(type => type != typeof(Profile) && typeof(Profile).IsAssignableFrom(type));

		autoMapperProfileTypes.Each(autoMapperProfileType =>
			container.RegisterType(typeof(Profile),
			autoMapperProfileType,
			autoMapperProfileType.FullName,
			new ContainerControlledLifetimeManager(),
			new InjectionMember[0]));
	}
}

With this sample extension method, you can register all AutoMapper profiles. In the RegisterAutoMapperProfiles is called method FromAssemblies of class AllClasses provided by Unity. On this way are we getting all types which inherits from the AutoMapper.Profiles and register them in the Unity container.

In the extension method RegisterAutoMapperType is registered each AutoMapper profile within instance of ConfigurationStore. Instance of ConfigurationStore is later injected in the constructor of class MappingEngine.

I leave it on the user which LifeTimeManager he want use within class MappingEngine, but I recommend ContainerControlledLifetimeManager, which correspond to the Singleton instance.

The unit test below show you the usage:

[TestMethod]
public void GetAllCustomers_UseDependencyInjectionContainer_AllCustomersAreReturned()
{
	//Arrange
	IUnityContainer unityContainer = DependencyInjectionContainerConfiguration.InitializeContainer(new ContainerControlledLifetimeManager());
	var customerRepository = unityContainer.Resolve<ICustomerRepository>();

	//Act
	IEnumerable<CustomerDomain> actualCustomers = customerRepository.GetAllCustomers();

	//Assert
	Mapper.AssertConfigurationIsValid();
	CollectionAssert.AreEqual(GetExpectedCustomers().ToList(), actualCustomers.ToList());
}
public static class DependencyInjectionContainerConfiguration
{
	public static IUnityContainer InitializeContainer(LifetimeManager autoMapperLifetimeManager = null)
	{
		IUnityContainer unityContainer = new UnityContainer();

		unityContainer.RegisterAutoMapperType(autoMapperLifetimeManager);
		unityContainer.RegisterType<IStorage, MemoryStorage>();
		unityContainer.RegisterType<ICustomerRepository, CustomerRepository>();

		return unityContainer;
	}
}

5 comments

  1. Hi,

    I have Used this code for Reference for registering profile with Unity but I got some configuration errors so I have added below mentioned line

    container.RegisterType(new ContainerControlledLifetimeManager(), new InjectionConstructor(typeof(ITypeMapFactory), MapperRegistry.Mappers));
    container.RegisterType( new ContainerControlledLifetimeManager());
    container.RegisterType();
    container.RegisterType();

    I have posted my Question on StackOverFlow

    http://stackoverflow.com/questions/29051841/automapper-with-unity-missing-type-map-configuration-or-unsupported-mapping

    Please help me If you can

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.