AutoMapper custom type convert using ConstructServicesUsing
Asked Answered
B

2

7

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

Biotype answered 21/6, 2013 at 17:51 Comment(6)
Does this help?: Mapper.CreateMap<Source, Destination>().ConstructUsingServiceLocator().ConvertUsing(new SourceDestinationTypeConverter());Lieu
Or Mapper.CreateMap<Source, Destination>().ConvertUsing(new SourceDestinationTypeConverter()); Both make the Assert succeed, but is any of them good for your situation?Lieu
Thanks, but I need to use the Mapper.Map-Method (instead of Mapper.CreateMap) as I need to pass in some runtime params.Biotype
Is it neccesary for you to use: Mapper.CreateMap<Source, Destination>().ConstructUsingServiceLocator(); ? Since I did get this working with "Mapper.CreateMap<Source, Destination>();"... But I guess you are passing those run time params through the service locator ?Lieu
I'm still facing problems about what you are trying to achieve. I've ran through a couple of code samples (all a bit modified to yours) and I manage to get alot of succesful results. Am I correct if I say that you are trying to use a IoC Container / Object Factory to create your instances of your destination objects for convertion. (since this is where the ConstructServicesUsing comes in). Here you are passing in the Converter object, which looks incorrect. I am having issues aswell with using both the IoC Container / factory AND a custom converter.Lieu
Thanks, using an IoC-Container I solved it as you have shown in your answer. I thought a factory might also work, but it didn't.Biotype
L
12

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

Lieu answered 23/6, 2013 at 22:21 Comment(1)
Thanks, but the Convert method of the SourceDestinationTypeConverter then never gets called.Biotype
S
0

I encountered the following error

InvalidCastException: Unable to cast object of type 'xxxTypeConverter' to type 'AutoMapper.ITypeConverter`2[System.String,System.DateTime]' while executing Automapper sample for CustomTypeConverter.

I fixed the issue by replacing

public class DateTimeTypeConverter : ITypeConverter<string, DateTime>

with

 public class DateTimeTypeConverter : AutoMapper.ITypeConverter<string, DateTime>

and similarly

public class TypeTypeConverter : ITypeConverter<string, Type>

with

public class TypeTypeConverter : AutoMapper.ITypeConverter<string, Type>
Saturnian answered 23/8, 2019 at 11:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.