1

I have a multitenant web application which has multiple modules. I am using autofac as IOC and Automapper to map my model and entities. In the following code I am able to add UserAuthenticationMapper profile to the automapper, but unable to add CustomerDataMapper profile. Autofac is adding just one mapper profile to the automapper MappingEngine and ignoring rest of it. Whats wrong I am doing here?

Here's my UserAuthentication Module:

 public class UserAuthenticationModule: Autofac.Module
 {
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<MappingEngine>().As<IMappingEngine>();

        builder.Register(ctx =>
        {
            var configStore = new ConfigurationStore
           (new TypeMapFactory(),  MapperRegistry.AllMappers());
            configStore.AddProfile<UserAuthenticationMapperProfile>();
            return configStore;
        })
        .AsImplementedInterfaces()
        .SingleInstance();

        var assembly = Assembly.GetExecutingAssembly();
        builder.RegisterAssemblyTypes(assembly)
               .Where(t => t.Name.EndsWith("Service"))
               .AsImplementedInterfaces()
               .SingleInstance();
      }
  }

CustomerData Modue:

  public class CustomerDataModule : Autofac.Module
  {
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<CustomerDataService>()
            .As<ICustomerDataService>()
            .InstancePerDependency();

        builder.RegisterType<MappingEngine>().As<IMappingEngine>();

        builder.Register(ctx =>
            {
                var configStore = new ConfigurationStore
                (new TypeMapFactory(), MapperRegistry.AllMappers());
                configStore.AddProfile<CustomerDataMapperProfile>();
                return configStore;
            })
               .AsImplementedInterfaces()
               .SingleInstance();
      }
   }

here is some additional code to show how i am loading these modules in Global.asax

    static IContainerProvider _containerProvider;

    public IContainerProvider ContainerProvider
    {
        get { return _containerProvider; }
    }

    void Application_Start(object sender, EventArgs e)
    {
        RouteTable.Routes.MapHttpRoute(
             name: "ActionApi",
             routeTemplate: "api/{controller}/{action}"
         );

        RouteTable.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = System.Web.Http.RouteParameter.Optional }
        );
      var builder = new ContainerBuilder();
      builder.RegisterModule<UserAuthenticationModule>();
      builder.RegisterModule<CustomerDataModule>();
      _containerProvider = new ContainerProvider(builder.Build());
     }

Error:

An exception of type 'AutoMapper.AutoMapperMappingException' occurred in AutoMapper.dll but was not handled in user code Missing type map configuration or unsupported mapping.\r\n\r\nMapping types:\r\nUser -> UserModel

rajcool111
  • 657
  • 4
  • 14
  • 36
  • Can you show us how you're registering these modules, how you resolve your mapping (e.g., how you verify that one profile is getting loaded but not the other), and tell us if you are seeing any error messages? – Travis Illig Dec 05 '13 at 22:42
  • I have edited my question with additional details and error message. I am getting this error for 'CustomerDataMapperProfile'. – rajcool111 Dec 06 '13 at 03:58

1 Answers1

6

I'm guessing the problem you have is that you're registering two ConfigurationStore instances but MappingEngine only takes one in as a constructor parameter.

When you register more than one of a thing in Autofac, it's "last in wins." So when you resolve a MappingEngine it'll pick the constructor like this:

public MappingEngine(IConfigurationProvider configurationProvider)

That only takes in one configuration provider (ConfigurationStore implements IConfigurationProvider), so you'll only get one. Which means it's the equivalent of doing:

container.Resolve<IConfigurationProvider>();

You'll only get one. If you needed to resolve all of them, you'd need to resolve an IEnumerable<IConfigurationProvider> from Autofac, like:

container.Resolve<IEnumerable<IConfigurationProvider>>();

But that won't solve your problem, it only describes what I think is happening.

What you need to do to get done what you want is to break things down a bit more. Instead of registering ConfigurationStore in each module, only register the profiles. Like this (shortened for readability):

public class CustomerDataModule : Autofac.Module
{
  protected override void Load(ContainerBuilder builder)
  {
    builder.RegisterType<CustomerDataMapperProfile>().As<Profile>();
  }
}

public class UserAuthenticationModule : Autofac.Module
{
  protected override void Load(ContainerBuilder builder)
  {
    builder.RegisterType<UserAuthenticationMapperProfile>().As<Profile>();
  }
}

Then you need to break out the common AutoMapper registrations from your modules. You don't want to double-register. Create a new module that handles registering the mapping engine and also does some dynamic resolution of the profiles.

public class AutoMapperModule : Autofac.Module
{
  protected override void Load(ContainerBuilder builder)
  {
    // Register the common thing once, not in each module.
    builder.RegisterType<MappingEngine>().As<IMappingEngine>();

    // Here's where you dynamically get all the registered profiles
    // and add them at the same time to the config store.
    builder.Register(ctx =>
      {
        var profiles = ctx.Resolve<IEnumerable<Profile>>();
        var configStore = new ConfigurationStore
            (new TypeMapFactory(), MapperRegistry.AllMappers());
        foreach(var profile in profiles)
        {
          configStore.AddProfile(profile);
        }
        return configStore;
      })
      .AsImplementedInterfaces()
      .SingleInstance();
  }
}

Make sure you register all the modules in your container:

 var builder = new ContainerBuilder();
 builder.RegisterModule<UserAuthenticationModule>();
 builder.RegisterModule<CustomerDataModule>();
 builder.RegisterModule<AutoMapperModule>();
 _containerProvider = new ContainerProvider(builder.Build());

Now you should have all of the profiles registered when you resolve the IMappingEngine.

Travis Illig
  • 23,195
  • 2
  • 62
  • 85
  • I used this code in my project with automapper 3.3.1.0 and it works, but I changed one thing: `MapperRegistry.AllMappers()` I changed on `MapperRegistry.Mappers`. To build everything just resolve `_containerProvider = new ContainerProvider(builder.Build()); _containerProvider.Resolve();` in a root . – Alezis Jul 22 '15 at 08:27
  • This looks a good solution, but in my service I noticed that ctx.Resolve>() is never called and consequently my mappers are not created. Any ideas why? – Bonomi Sep 18 '15 at 11:19
  • @Bonomi You may have more than one IConfigurationStore registered so the one getting resolved is not the one with your profiles? That's a guess. Would need to see the code. Probably not something I can answer in a comment thread. – Travis Illig Sep 18 '15 at 12:57
  • @TravisIllig I passed the "IMappingEngine mapper" as a entry in the controller and instead of calling Mapper.CreateMap() I called _mapper.CreateMap() and it worked! Thanks anyway – Bonomi Sep 18 '15 at 14:00