27

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)?

Wojciech Markowski
  • 1,074
  • 1
  • 11
  • 15

10 Answers10

41

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.

Jason More
  • 6,983
  • 6
  • 43
  • 52
  • This seems to be mutually exclusive with 'ConstructServicesUsing' method. Struggling with passing dependencies atm. – Arnis Lapsa Oct 05 '10 at 11:37
  • Yeah I'm not passing in any dependencies in my maps. You could always register them in an IoC container and have it resolve the dependencies for you. We would have done that but there was not any need for it. – Jason More Oct 05 '10 at 13:04
  • 2
    Arnis L - just change configuration.AddProfile(Activator.CreateInstance(profile) as Profile); to configuration.AddProfile(ServiceLocator.Current.GetInstance(profile) as Profile); (or similar, obviously it may depend on which IoC container you're using) to enable dependency injection. Great answer. – Paul Suart Oct 21 '10 at 08:11
23

In version 9 of AutoMapper it can be done this way

var configuration = new MapperConfiguration(cfg =>
{
    // Add all Profiles from the Assembly containing this Type
    cfg.AddMaps(typeof(MyApp.SomeClass));
});

If you are using ASP.NET Core there is a helper extension to register all Profiles in Startup.ConfigureServices

// UI project
services.AddAutoMapper(Assembly.GetExecutingAssembly());

or

// Another assembly that contains a type
services.AddAutoMapper(Assembly.GetAssembly(typeof(MyApp.SomeClass)));
Rosco
  • 2,108
  • 17
  • 17
18

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

Martino Bordin
  • 1,412
  • 1
  • 14
  • 29
9

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.

Jimmy Bogard
  • 26,045
  • 5
  • 74
  • 69
  • Thanks for AutoMapper - I and many others would be lost without it. Is there a blog post (or email thread / article / SO post) on how `Profile` and friends should be used out there that you're aware of? Right now on SO, things are a bit confusing as there are lots of v1 era examples but previous little v2 stuff. Even a one sentence thing like 'rig your DI to pick up IXXXInitialize impls, and for those to be fed to the ctor of XXXConfiguration and then have e.g. Controllers requiring mappers bound to IMapper which is implemented by IConfiguration' or something of that ilk. – Ruben Bartelink Jun 12 '12 at 07:08
2

In .NET Core:

    services.AddSingleton(this.CreateMapper());
    //...
    private IMapper CreateMapper()
            => new MapperConfiguration(config => config.AddMaps(Assembly.Load("Your.Project.App")))
            .CreateMapper();
1

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);
            }
        }
    }
}
epitka
  • 17,275
  • 20
  • 88
  • 141
1

Just to add to @Rosco answer you can use typeof(class) instead of assembly. Any class will work.

services.AddAutoMapper(typeof(Startup));

Then you do not need to add Reflection reference.

If you have Profiles in multiple assemblies, you can use it like this.

services.AddAutoMapper(typeof(Startup), typeof(User));
eMbs
  • 79
  • 1
  • 6
1
//AutoMapper 12.0.0
using Microsoft.Extensions.DependencyInjection;

//if the profiles are in the same assembly as the StartUp class

services.AddAutoMapper(typeof(Startup));

//else

services.AddAutoMapper(typeof(AnyProfile));
Diego Lobo
  • 156
  • 7
0
 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);
    }
}
0

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();
Cirem
  • 840
  • 1
  • 11
  • 15