Base
Base

Reputation: 1061

Automapper & Autofac typeconverter - does not have a default constructor

I'm having issues getting aufotac to inject into my autopmapper typeconverters. I've tried some different ways but I'm currently stuck using the code below. The closest I've come to finding a solution is the code below (borrowed small bits from http://thoai-nguyen.blogspot.se/2011/10/autofac-automapper-custom-converter-di.html). His sample seems to work 1:1 but haven't been able to locate what I'm missing. As usual extracted the relevant bits, let me know if it's insufficient.

My autofac bootstrapper:

public class AutoFacInitializer
    {
        public static void Initialize()
        {
            //Mvc
            var MvcContainer = BuildMvcContainer();
            DependencyResolver.SetResolver(new AutofacDependencyResolver(MvcContainer));

            //Web API
            var ApiContainer = BuildApiContainer();
            var ApiResolver = new AutofacWebApiDependencyResolver(ApiContainer);
            GlobalConfiguration.Configuration.DependencyResolver = ApiResolver;
        }

        private static IContainer BuildApiContainer()
        {
            var builder = new ContainerBuilder();
            var assembly = Assembly.GetExecutingAssembly();
            builder.RegisterApiControllers(assembly);
            return BuildSharedDependencies(builder, assembly);
        }

        private static IContainer BuildMvcContainer()
        {
            var builder = new ContainerBuilder();
            var assembly = typeof (MvcApplication).Assembly;
            builder.RegisterControllers(assembly);
            builder.RegisterFilterProvider();
            return BuildSharedDependencies(builder, assembly);
        }

        private static IContainer BuildSharedDependencies(ContainerBuilder builder, Assembly assembly)
        {
            //----Build and return container----
            IContainer container = null;

            //Automapper
            builder.RegisterAssemblyTypes(assembly).AsClosedTypesOf(typeof(ITypeConverter<,>)).AsSelf();
            AutoMapperInitializer.Initialize(container);
            builder.RegisterAssemblyTypes(assembly).Where(t => typeof(IStartable).IsAssignableFrom(t)).As<IStartable>().SingleInstance();

            //Modules
            builder.RegisterModule(new AutofacWebTypesModule());
            builder.RegisterModule(new NLogLoggerAutofacModule());

            //Automapper dependencies
            builder.Register(x => Mapper.Engine).As<IMappingEngine>().SingleInstance();

            //Services, repos etc
            builder.RegisterGeneric(typeof(SqlRepository<>)).As(typeof(IRepository<>)).InstancePerDependency();
            
            container = builder.Build();
            return container;
        }
    }

My Automap bootstrapper / initializer:

namespace Supportweb.Web.App_Start
{
    public class AutoMapperInitializer
    {
        public static void Initialize(IContainer container)
        {
            Mapper.Initialize(map =>
            {
                map.CreateMap<long?, EntityToConvertTo>().ConvertUsing<LongToEntity<NavigationFolder>>();

                map.ConstructServicesUsing(t => container.Resolve(t)); 
            });
            Mapper.AssertConfigurationIsValid();
        }
    }
}

The typeconverter that im trying to get working:

public class LongToEntity<T> : ITypeConverter<long?, T>
    {
        private readonly IRepository<T> _repo;

        public LongToEntity(IRepository<T> repo)
        {
            _repo = repo;
        }

        public T Convert(ResolutionContext context) 
        {
            long id = 0;
            if (context.SourceValue != null)
                id = (long)context.SourceValue;
            return _repo.Get(id);
        }
    }

Except for the converter all mappings works fine. The error seems to indicate that I'm lacking an ioc reference but I've tried but the mentioned ITypeConverter<,> and LongToEntity<> and variations which doesn't seem to help.

Upvotes: 4

Views: 3396

Answers (1)

nemesv
nemesv

Reputation: 139758

You have three problems with your current code:

  1. You need to call ConstructServicesUsing before registering any mapping as described in the linked article:

    The tricky thing is we need to call that method before we register mapper classes.

    So the correct Mapper.Initialize is the follwing:

    Mapper.Initialize(map =>
            {
                map.ConstructServicesUsing(t => container.Resolve(t));  
    
                map.CreateMap<long?, EntityToConvertTo>()
                    .ConvertUsing<LongToEntity<NavigationFolder>>();
            });
    
  2. Because your LongToEntity<T> is an open generic you cannot use AsClosedTypesOf but you need to use here also the RegisterGeneric for the registration:

    So change your ITypeConverter<,> registration from:

     builder.RegisterAssemblyTypes(assembly)
            .AsClosedTypesOf(typeof(ITypeConverter<,>)).AsSelf();
    

    To use the RegisterGeneric method:

     builder.RegisterGeneric(typeof(LongToEntity<>)).AsSelf();
    
  3. Because you have moved the Automapper initialization into a separate method AutoMapperInitializer.Initialize you cannot use the clojure trick from the article, so you need to call it after you have created the container:

     container = builder.Build();
     AutoMapperInitializer.Initialize(container);
     return container;
    

Upvotes: 8

Related Questions