g t
g t

Reputation: 7463

Custom object-to-string mapper in AutoMapper

Using AutoMapper (v6.2.2, plus AutoMapper.Extensions.Microsoft.DependencyInjection) I want to use my existing system TypeConverter classes to convert from certain data models to strings.

Although the internal MapperRegistry contains a TypeConverterMapper that should be able to do this automatically (without the need for any manual configuration it says here), it never gets called as the StringMapper takes precedence when the destination type is a string.

Other (older) answers suggest changing the mapping objects in the MapperRegistry, but it appears that this class has since been made internal.

Can I change the precedence of (or remove) the different in-built mapper classes in AutoMapper?


As a workaround, I also tried creating a map in my Profile that would attempt to convert any object to a string using type converters:

private class ApiTypeConverter : ITypeConverter<object, string>
{
     public string Convert(object source, string destination, ResolutionContext context)
     {
        TypeConverter sourceTypeConverter = TypeDescriptor.GetConverter(source.GetType());
        if (sourceTypeConverter.CanConvertTo(typeof(string)))
        {
           return (string)sourceTypeConverter.ConvertTo(source, typeof(string));
        }

        return default(string);
     }
}

which I configured to be used with:

CreateMap<object, string>().ConvertUsing<ApiTypeConverter>();

But this has not worked as expected yet either. Ideally I'd only do this for members which could be converted using a condition, something like:

ForAllMaps((m, e) => e.ForAllMembers(opt => opt.Condition(HasConverter))
                      .ConvertUsing<ApiTypeConverter>());

However, this is not possible (the ForAllMembers method returns void).


As I am using AutoMapper.Extensions.Microsoft.DependencyInjection I was able to use the additionalInitAction parameter to change the mappers:

services.AddAutoMapper(InitAction, GetType().Assembly);

private void InitAction(IMapperConfigurationExpression configuration)
{
    var mapper = configuration.Mappers.OfType<StringMapper>().First();
    configuration.Mappers.Remove(mapper);
}

This led everything to work as expected, but seems a bit of a hack, as other maps may be relying on the StringMapper.

Upvotes: 0

Views: 1082

Answers (2)

g t
g t

Reputation: 7463

If using dependency injection, the configuration can be changed when adding the AutoMapper to the services:

services.AddAutoMapper(InitializeMapper, typeof(Startup).Assembly);

Then add an appropriate initialization:

private static void InitializeMapper(IMapperConfigurationExpression cfg)
{
    StringMapper stringMapper = cfg.Mappers.OfType<StringMapper>().First();
    int index = cfg.Mappers.IndexOf(stringMapper);

    cfg.Mappers.Insert(index, new CustomObjectMapper());
}

Upvotes: 1

Lucian Bargaoanu
Lucian Bargaoanu

Reputation: 3516

You can use configuration.Mappers to change the built-in mappers' list, but a map from object to string should also work.

Mapper.Initialize(cfg =>
{
    var mappers = cfg.Mappers;
});

Upvotes: 1

Related Questions