Reputation: 6973
I am using AutoMapper with ITypeConverter and I want to keep everything within Castle Windsor. In one assembly I have my ITypeCoverter and I load them using this approach:
container
.Register(Types.FromAssembly(Assembly.GetExecutingAssembly())
.BasedOn(typeof(ITypeConverter<,>)));
So far so good I can see that Windsor is loading all my converters properly.
Then I register AutoMapper within Windsor
Mapper.Initialize(m => m.ConstructServicesUsing(container.Resolve));
container.Register(Component.For<IMappingEngine>().Instance(Mapper.Engine));
But when I ask an instance of IMappingEngine the conventions are not loaded. Do I miss something here?
Upvotes: 2
Views: 2240
Reputation: 179
I do the following to make Automapper works.
1st at registration point:
foreach (var automapperProfileType in types.Where(t => t.IsInterface == false && typeof(Profile).IsAssignableFrom(t)))
{
container.Register(
Component.For(typeof(Profile))
.ImplementedBy(automapperProfileType)
.LifestyleTransient()
.Named("AutoMapper / Profile / " + automapperProfileType.FullName));
automapperProfileTypes.Add(automapperProfileType);
}
And then then after initialization of all types:
foreach (var profileMap in container.ResolveAll<Profile>())
{
Mapper.AddProfile(profileMap);
}
Hope that helps.
Upvotes: 0
Reputation: 919
I got AutoMapper and Windsor to play nicely together with both Profiles and TypeConverters. I think this should be agnostic enough for you, since AutoMapper will automatically pick up any implementations of ITypeConverter.
Let me know if it solves your problem.
First I registered AutoMapper with Windsor:
public class AutoMapperInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
// Replace by whatever assembly contains your ITypeConverter, IValueResolver and Profile implementations
var myAssembly = Classes.FromAssemblyNamed("MyAssembly");
// Register AutoMapper such that it uses a singleton configuration
container.Register(
myAssembly.BasedOn(typeof(ITypeConverter<,>)).WithServiceSelf(),
myAssembly.BasedOn<IValueResolver>().WithServiceBase(),
myAssembly.BasedOn<Profile>().WithServiceBase(),
Component.For<IEnumerable<IObjectMapper>>().UsingFactoryMethod(() => MapperRegistry.Mappers),
Component.For<ConfigurationStore>()
.LifestyleSingleton()
.UsingFactoryMethod(x =>
{
var typeMapFactory = x.Resolve<ITypeMapFactory>();
var mappers = x.Resolve<IEnumerable<IObjectMapper>>();
ConfigurationStore configurationStore = new ConfigurationStore(typeMapFactory, mappers);
configurationStore.ConstructServicesUsing(x.Resolve);
configurationStore.AssertConfigurationIsValid();
return configurationStore;
}),
Component.For<IConfigurationProvider>().UsingFactoryMethod(x => x.Resolve<ConfigurationStore>()),
Component.For<IConfiguration>().UsingFactoryMethod(x => x.Resolve<ConfigurationStore>()),
Component.For<IMappingEngine>().ImplementedBy<MappingEngine>().LifestyleSingleton(),
Component.For<ITypeMapFactory>().ImplementedBy<TypeMapFactory>()
);
// Add all Profiles
var configuration = container.Resolve<IConfiguration>();
container.ResolveAll<Profile>().ToList().ForEach(configuration.AddProfile);
}
}
Then I created a Profile where I registered all the ITypeConverter implementations using a bit of reflection:
public class TypeConverterProfile : Profile
{
private readonly IConfiguration _configuration;
public TypeConverterProfile(IConfiguration configuration)
{
_configuration = configuration;
}
protected override void Configure()
{
// Replace by whatever assembly contains your ITypeConverter implementations
var myAssembly = Classes.FromAssemblyNamed("MyAssembly");
var typeConverters =
from x in myAssembly.GetTypes()
from type in x.GetInterfaces()
where type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ITypeConverter<,>)
select new
{
Type = x,
Source = type.GetGenericArguments()[0],
Destination = type.GetGenericArguments()[1]
};
// Apply type converters to AutoMapper configuration
foreach (var typeConverter in typeConverters)
_configuration.CreateMap(typeConverter.Source, typeConverter.Destination).ConvertUsing(typeConverter.Type);
}
}
AutoMapper will now automatically pick up and use any Profiles and ITypeConverters implementations in "MyAssembly".
As an example, you may have a Profile:
public class MapProfile : Profile
{
private readonly IConfiguration _configuration;
public MapProfile(IConfiguration configuration)
{
_configuration = configuration;
}
protected override void Configure()
{
// Create an auto-map from SearchResultModel -> SearchResult
_configuration.CreateMap<SearchResultModel, SearchResult>();
}
}
And an ITypeConverter:
public class SearchRequestConverter : ITypeConverter<SearchRequest, SearchRequestModel>
{
private readonly IConfiguration _configuration;
public MapProfile(IConfiguration configuration)
{
// Also in the ITypeConverter you may have the IConfiguration injected in case you need it
_configuration = configuration;
}
public SearchRequestModel Convert(ResolutionContext context)
{
var model = (SearchRequest) context.SourceValue;
return new SearchRequestModel
{
Results = model.Results
};
}
}
Finally you perform the actual mapping in your class by having a dependency on the IMappingEngine:
public class FolderRepository
{
private readonly IMappingEngine _mappingEngine;
public FolderRepository(IMappingEngine mappingEngine)
{
_mappingEngine = mappingEngine;
}
public SearchResult Search(SearchRequest searchRequest)
{
// This mapping will use the ITypeConverter implementation
SearchRequestModel searchRequestModel = _mappingEngine.Map<SearchRequestModel>(searchRequest);
SearchResultModel searchResultModel = Search(searchRequestModel);
// This mapping will use the auto-map from the Profile
SearchResult searchResult = _mappingEngine.Map<SearchResultModel>(searchResultModel);
return searchResult;
}
private SearchResultModel Search(SearchRequestModel searchRequestModel)
{
SearchResultModel result = new SearchResultModel();
// ... perform search ...
return result;
}
}
To have more ITypeConverters, you just have to implement them, and they will automagically be picked up by Windsor and AutoMapper. Manual auto-maps (CreateMap<>) can be added in a Profile.
Does that solve your problem?
Upvotes: 3
Reputation: 15737
I see no code that creates/ setups mappings. Neither Mapper.CreateMap<Source, Destination>();
nor configuration.CreateMap<Source, Destination>();
Unfortunately, there isn't enough information about using IoC and AutoMapper together. You can find only one an example of usage AutoMapper and Structure Map in the official samples.
There is also my example of usage AutoMapper and Castle Windsor. It is the similar to previous one, but Castle Windsor is used instead of Structure Map.
/// <summary>
/// Castle Windsor example.
/// </summary>
/// <remarks>
/// StructureMap example
/// see https://github.com/AutoMapper/AutoMapper/blob/develop/src/AutoMapperSamples/CastleWindsorIntegration.cs
/// </remarks>
public class CastleWindsorIntegration
{
private readonly IWindsorContainer container;
public CastleWindsorIntegration()
{
container = new WindsorContainer();
}
[Fact]
public void Example()
{
container.Install(new ConfigurationInstaller());
var configuration1 = container.Resolve<IConfiguration>();
var configuration2 = container.Resolve<IConfiguration>();
configuration1.Should().BeSameAs(configuration2);
var configurationProvider = container.Resolve<IConfigurationProvider>();
configurationProvider.Should().BeSameAs(configuration1);
var configuration = container.Resolve<ConfigurationStore>();
configuration.Should().BeSameAs(configuration1);
configuration1.CreateMap<Source, Destination>();
var engine = container.Resolve<IMappingEngine>();
var destination = engine.Map<Source, Destination>(new Source { Value = 15 });
destination.Value.Should().Be(15);
}
[Fact]
public void Example2()
{
container.Install(new MappingEngineInstaller());
Mapper.Reset();
Mapper.CreateMap<Source, Destination>();
var engine = container.Resolve<IMappingEngine>();
var destination = engine.Map<Source, Destination>(new Source { Value = 15 });
destination.Value.Should().Be(15);
}
public class Source
{
public int Value { get; set; }
}
public class Destination
{
public int Value { get; set; }
}
public class ConfigurationInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IEnumerable<IObjectMapper>>()
.LifestyleSingleton()
.UsingFactoryMethod(() => MapperRegistry.Mappers),
Component.For<ConfigurationStore>().ImplementedBy<ConfigurationStore>(),
Component.For<IConfigurationProvider>().UsingFactoryMethod(k => k.Resolve<ConfigurationStore>()),
Component.For<IConfiguration>().UsingFactoryMethod(k => k.Resolve<ConfigurationStore>()),
Component.For<IMappingEngine>().ImplementedBy<MappingEngine>(),
Component.For<ITypeMapFactory>().ImplementedBy<TypeMapFactory>());
}
}
public class MappingEngineInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IMappingEngine>().UsingFactoryMethod(() => Mapper.Engine));
}
}
}
In order to make the test green it is needed to use Mapper.CreateMap<Source, Destination>().ConvertUsing<SourceToDestinqtionConvertor>();
for example.
[Fact]
public void Example3()
{
container
.Register(Types.FromAssembly(Assembly.GetExecutingAssembly())
.BasedOn(typeof(ITypeConverter<,>)));
Mapper.Initialize(m => m.ConstructServicesUsing(container.Resolve));
container.Register(Component.For<IMappingEngine>().Instance(Mapper.Engine));
var engine = container.Resolve<IMappingEngine>();
Mapper.CreateMap<Source, Destination>().ConvertUsing<SourceToDestinqtionConvertor>();
var destination = engine.Map<Source, Destination>(new Source { Value = 15 });
destination.Value.Should().Be(15);
}
public class SourceToDestinqtionConvertor : ITypeConverter<Source, Destination>
{
public Destination Convert(ResolutionContext context)
{
throw new System.NotImplementedException();
}
}
EDIT:
In order be able to resolve 'ITypeConverter' it is needed to specify which service are going to be used.
container
.Register(Types.FromAssembly(Assembly.GetExecutingAssembly())
.BasedOn(typeof(ITypeConverter<,>))
.WithService.AllInterfaces());
// Makes sure that type converter can be resolved
var resolver = container.Resolve<ITypeConverter<Source, Destination>>();
resolver.Should().BeOfType<SourceToDestinqtionConvertor>();
Upvotes: 1