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>()
In order to get AutoMapper to use Windsor to create the target type, you need to configure two things:
- Tell AutoMapper to construct services using Windsor
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);
For anyone requiring this using xamarin 3.6, prism 7.1 and automapper 8.1, this is what worked for me.
In the App.xml.cs file
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
/// other registrations ...
containerRegistry.RegisterSingleton<IMapperProvider, MapperProvider>();
containerRegistry.RegisterInstance(typeof(IMapper), GetMapper(containerRegistry));
}
/// <summary>
/// This function required in order for injection into custom automapper resolvers
/// </summary>
private IMapper GetMapper(IContainerRegistry container)
{
var mp = container.GetContainer().Resolve<IMapperProvider>(new[] { container });
return mp.GetMapper();
}
The mapper provider looks like this:
public class MapperProvider : IMapperProvider
{
private readonly IContainerRegistry _container;
public MapperProvider(IContainerRegistry container)
{
_container = container;
}
public IMapper GetMapper()
{
var config = new MapperConfiguration(cfg =>
{
cfg.ConstructServicesUsing(t => _container.GetContainer().Resolve(t));
// any custom profile statement such as
cfg.AddProfile<MappingSourcesProfile>();
// ....
});
return config.CreateMapper();
}
}
Now my custom resolvers works, for example:
public class BarcodesResolver : IValueResolver<repo.Book, Book, List<Barcode>>
{
private readonly IMapper _mapper;
public BarcodesResolver(IMapper mapper)
{
_mapper = mapper;
}
public List<Barcode> Resolve(repo.Book source, Book destination, List<Barcode> destMember, ResolutionContext context)
{
repo.BookAttributes groupedAttribs = JsonConvert.DeserializeObject<repo.BookAttributes>(source.BookAttributes);
return _mapper.Map<List<repo.Barcode>, List<Barcode>>(groupedAttribs.Barcodes);
}
}
The hard parts to work out here were how to specify that the containerRegistry needed to be passed into the constructor of the MapperProvider. There may be a better way of doing this but at least this works.
Also arriving at the line cfg.ConstructServicesUsing(t => _container.GetContainer().Resolve(t));
was quite obscure as there seem to be few examples out there.