views:

285

answers:

3

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

+2  A: 

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
A: 

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
With this - I think You cannot control life cycle of profile dependencies.
Arnis L.
+1  A: 

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
This seems to be mutually exclusive with 'ConstructServicesUsing' method. Struggling with passing dependencies atm.
Arnis L.
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
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