Search code examples
c#dependency-injectionautomappercastle-windsor

Instantiating types using AutoMapper with a DI container


Please see the code below:

public class Test : ITest
    {
        public ITest2 _iTest2;
        public int _id;
        public string _name;

        public Test(ITest2 test2)
        {
            _iTest2 = test2;
        }
    }

    public interface ITest
    {
    }

    public class Test2 : ITest2
    {
    }

    public interface ITest2
    {

    }

    public class Test3 : ITest3
    {
        public int _id;
        public string _name;
    }

    public interface ITest3
    {

    }

I have the following in my Global.asax:

Mapper.Initialize(m =>
            {  
 m.CreateMap<DataLayer.Test3, BusinessLayer.Test>().ConstructUsing(opt => new BusinessLayer.Test(new BusinessLayer.Test2()));
});

I can map the types in my client app doing this:

cfg.CreateMap<DataLayer.Test3, BusinessLayer.Test>().ConstructUsing(opt => new BusinessLayer.Test(new BusinessLayer.Test2()));

How can I map the types using Castle Windsor instead of having to use the new keyword for Test and Test2?

I read another answer and someone suggested doing this:

 public void Install(IWindsorContainer container, IConfigurationStore store)
    {

        container.Register(Types.FromAssembly(Assembly.GetExecutingAssembly()).BasedOn(typeof(IValueResolver<,,>)));
        // container.Register(Types.FromAssembly(Assembly.GetExecutingAssembly()).BasedOn<IValueResolver>());
        container.Register(Types.FromThisAssembly().BasedOn<Profile>().WithServiceBase());
        var profiles = container.ResolveAll<Profile>();

        // Add your list of profiles to the mapper configuration here
        Mapper.Initialize(m => {
            m.ConstructServicesUsing(container.Resolve);
            profiles.ToList().ForEach(p => m.AddProfile(p));
        });

        // I'm not sure about this as I haven't used AutoMapper for a while,
        // but I assume you want to resolve the static mapper instance
        container.Register(Component.For<IMapper>().Instance(Mapper.Instance));
    }

Do I have to do this:

cfg.CreateMap<DataLayer.Test3, BusinessLayer.Test>().ConstructUsing(opt => new BusinessLayer.Test(new BusinessLayer.Test2()));

or should AutoMapper be able to map the types using this:

cfg.CreateMap<DataLayer.Test3, BusinessLayer.Test>()

Solution

  • In order to get AutoMapper to use Windsor to create the target type, you need to configure two things:

    1. Tell AutoMapper to construct services using Windsor
    2. Tell AutoMapper (per-mapping) to actually use the above configuration

       var container = new WindsorContainer();
      
          Mapper.Initialize(m =>
          {
              m.ConstructServicesUsing(container.Resolve);
      
              m.CreateMap<Test3, ITest>().ConstructUsingServiceLocator(); // This is important!
      
          });
      
          container.Register(Component.For<ITest>().ImplementedBy<Test>());
          container.Register(Component.For<ITest2>().ImplementedBy<Test2>());
          container.Register(Component.For<ITest3>().ImplementedBy<Test3>());
      
          var test3 = new Test3();
          var test1 = Mapper.Instance.Map<Test3, ITest>(test3);