Is there any way to auto-configue Automapper to scan for all profiles in namespace/assembly? What I would like to do is to add mapping profiles to AutoMapper from given assembly filtered by given interface, something like Scan Conventions in StructureMap:
public static void Configure()
{
ObjectFactory.Initialize(x =>
{
// Scan Assembly
x.Scan(
scanner =>
{
scanner.TheCallingAssembly();
scanner.Convention<MyCustomConvention>();
scanner.WithDefaultConventions();
});
// Add Registries
x.AddRegistry(new SomeRegistry());
});
Debug.WriteLine(ObjectFactory.WhatDoIHave());
}
public class MyCustomConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
if (!type.CanBeCastTo(typeof(IMyType)))
{
return;
}
string name = type.Name.Replace("SomeRubishName", String.Empty);
registry.AddType(typeof(IMyType), type, name);
}
I've tried to use SelfConfigure but can't find any documentation on how to use it to filter out profiles:
public static void Configure()
{
Mapper.Initialize(x =>
{
// My Custom profile
x.AddProfile<MyMappingProfile>();
// Scan Assembly
x.SelfConfigure(Assembly.GetCallingAssembly());
});
}
Another question is how can I report all maps/profiles already initialized (something like ObjectFactory.WhatDoIHave() in StructureMap)?
I found this post while searching as well, but this is how I implemented an auto mapping scheme:
public class MyCustomMap : Profile
{
protected override void Configure()
{
CreateMap<MyCustomViewModel, MyCustomObject>()
.ForMember(dest => dest.Phone,
opt => opt.MapFrom(
src => src.PhoneAreaCode + src.PhoneFirstThree + src.PhoneLastFour));
}
}
public static class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x => GetConfiguration(Mapper.Configuration));
}
private static void GetConfiguration(IConfiguration configuration)
{
var profiles = typeof(MyCustomMap).Assembly.GetTypes().Where(x => typeof(Profile).IsAssignableFrom(x));
foreach (var profile in profiles)
{
configuration.AddProfile(Activator.CreateInstance(profile) as Profile);
}
}
}
So when my application starts, all I call is
AutoMapperConfiguration.Configure();
And all my maps are registered.
In the latest versions of AutoMapper it's possible to register multiple Profile scanning one or more assemblies :
Mapper.Initialize(x => x.AddProfiles(typeof(MyMappingProfile).Assembly));
Tested with AutoMapper v. 6.0.2.0
Yeah, that would be fantastic...and exactly what I'm overhauling for V2. Scanning, registration, conventions etc.
There's not a good "What do I have" feature, but I think it would definitely be worth adding.
I have it like this, don't know if it is the best way but it works very well on pretty large project.
public class AutoMapperGlobalConfiguration : IGlobalConfiguration
{
private AutoMapper.IConfiguration _configuration;
public AutoMapperGlobalConfiguration(IConfiguration configuration)
{
_configuration = configuration;
}
public void Configure()
{
//add all defined profiles
var query = this.GetType().Assembly.GetExportedTypes()
.Where(x => x.CanBeCastTo(typeof(AutoMapper.Profile)));
_configuration.RecognizePostfixes("Id");
foreach (Type type in query)
{
_configuration.AddProfile(ObjectFactory.GetInstance(type).As<Profile>());
}
//create maps for all Id2Entity converters
MapAllEntities(_configuration);
Mapper.AssertConfigurationIsValid();
}
private static void MapAllEntities(IProfileExpression configuration)
{
//get all types from the SR.Domain assembly and create maps that
//convert int -> instance of the type using Id2EntityConverter
var openType = typeof(Id2EntityConverter<>);
var idType = typeof(int);
var persistentEntties = typeof(SR.Domain.Policy.Entities.Bid).Assembly.GetTypes()
.Where(t => typeof(EntityBase).IsAssignableFrom(t))
.Select(t => new
{
EntityType = t,
ConverterType = openType.MakeGenericType(t)
});
foreach (var e in persistentEntties)
{
var map = configuration.CreateMap(idType, e.EntityType);
map.ConvertUsing(e.ConverterType);
}
}
}
}
public class AutoMapperAdapter : IMapper
{
private readonly MapperConfigurationExpression _configurationExpression =
new MapperConfigurationExpression();
public void AssertConfigurationIsValid() { Mapper.AssertConfigurationIsValid(); }
public void CreateMap<TSource, TDestination>()
{
_configurationExpression.CreateMap<TSource, TDestination>();
}
public void Initialize() { Mapper.Initialize(_configurationExpression); }
public TDestination Map<TDestination>(object source)
{
return Mapper.Map<TDestination>(source);
}
}
Similar to @Martino's answer, but with a MapperConfiguration object. This will add all profiles from the assembly that contains the type MyProfile.
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfiles(typeof(MyProfile));
});
var mapper = config.CreateMapper();
In .NET Core:
services.AddSingleton(this.CreateMapper());
//...
private IMapper CreateMapper()
=> new MapperConfiguration(config => config.AddMaps(Assembly.Load("Your.Project.App")))
.CreateMapper();