AutoMapper custom type convert using ConstructServicesUsing

Alexander Zeitler picture Alexander Zeitler · Jun 21, 2013 · Viewed 33.5k times · Source

According to the AutoMapper Documentation, I should be able to create and use an instance of a Custom Type Converter using this:

var dest = Mapper.Map<Source, Destination>(new Source { Value = 15 },
    opt => opt.ConstructServicesUsing(childContainer.GetInstance));

I have the following source and destination types:

public class Source {
    public string Value1 { get; set; }
    public string Value2 { get; set; }
    public string Value3 { get; set; }
}

public class Destination {
    public int Value1 { get; set; }
    public DateTime Value2 { get; set; }
    public Type Value3 { get; set; }
}

And the following type converters:

public class DateTimeTypeConverter : ITypeConverter<string, DateTime> {
    public DateTime Convert(ResolutionContext context) {
        return System.Convert.ToDateTime(context.SourceValue);
    }
}

public class SourceDestinationTypeConverter : ITypeConverter<Source, Destination> {
    public Destination Convert(ResolutionContext context) {
        var dest = new Destination();
        // do some conversion
        return dest;
    }
}

This simple test should assert that one of the date properties get converted correctly:

[TestFixture]
public class CustomTypeConverterTest {
    [Test]
    public void ShouldMap() {
        Mapper.CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter());
        Mapper.CreateMap<Source, Destination>().ConstructUsingServiceLocator();

        var destination =
        Mapper.Map<Source, Destination>(
        new Source { Value1 = "15", Value2 = "01/01/2000", }, 
            options => options.ConstructServicesUsing(
                type => new SourceDestinationTypeConverter())
        ); // exception is thrown here

        Assert.AreEqual(destination.Value2.Year, 2000);
    }
}

But I already get an exception before the assert happens:

System.InvalidCastException : Unable to cast object of type 'SourceDestinationTypeConverter ' to type 'Destination'.

My question now is, how do I use a custom type converter using ConstructServicesUsing()?

Answer

Frederik Prijck picture Frederik Prijck · Jun 24, 2013

I tested this code and got this working using the following code:

public void TestMethod1()
    {
        Mapper.CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter());
        Mapper.CreateMap<string, Type>().ConvertUsing(new StringTypeConverter());
        Mapper.CreateMap<string, int>().ConvertUsing(new StringIntConverter());
        Mapper.CreateMap<Source, Destination>();

        var destination =
        Mapper.Map<Source, Destination>(
        new Source { Value1 = "15", Value2 = "01/01/2000", Value3 = "System.String" },
            options => options.ConstructServicesUsing(type => new SourceDestinationTypeConverter())
        );

        Assert.AreEqual(destination.Value2.Year, 2000);
    }

And the extra Converters:

 public class StringTypeConverter : ITypeConverter<string, Type>
{
    public Type Convert(ResolutionContext context)
    {
        return Type.GetType(context.SourceValue.ToString());
    }
}

public class StringIntConverter : ITypeConverter<string, int>
{
    public int Convert(ResolutionContext context)
    {
        return Int32.Parse(context.SourceValue.ToString());
    }
}

Automapper was missing a mapping for String to Type and String to Int. Also I had to remove the following line

Mapper.CreateMap<Source, Destination>().ConstructUsingServiceLocator();

and replace it by

Mapper.CreateMap<Source, Destination>();

I'm not aware of the "ConstructUsingServiceLocator()" option, but leaving it out works in this case... (I have no idea whether leaving it out will bring up other issues for u. Until now I have not yet used this option when working with Automapper.)

Please note I had to add the "Value3" parameter since the convertion would fail... Converting a NULL value to a Type can be pretty hard... (And I am not aware of what kind of conversion has to happen here...)