1
votes

I'm having trouble implementing Automapper conversion in a situation where the source is a class which should be mapped to one of two derived classes based on a value on the source.

Here's a simplification of my classes:

public class FooContainerDTO
{
    public FooDTO Foo { get; set; } 
}

public class FooDTO
{
    public string Type { get; set; }

    //some properties..
}

public class FooContainer
{
    public FooBase Foo { get; set; }
}

public abastract class FooBase
{
    //some properties..
}

public class FooDerived1 : FooBase
{
    //some properties
}

public class FooDerived2 : FooBase
{
    //some properties
}

I'm using non-static Automapper so I create a MapperConfiguration from several Profiles at boot and inject the IMapper instance into my DI-container. I want Automapper to map FooDTO to FooDerived1 when its Type property is "der1" and to FooDerived2 when it is "der2".

I've seen examples on this using the static api, something like this:

 Mapper.CreateMap<FooContainerDTO, FooContainer>();
        //ForMember configurations etc.

 Mapper.CreateMap<FooDTO, FooDerived1>();
        //ForMember configurations etc.

 Mapper.CreateMap<FooDTO, FooDerived2>();
        //ForMember configurations etc.

 Mapper.CreateMap<FooDTO, FooBase>()
       .ConvertUsing(dto => dto.Type == "der1"
           ? (FooBase) Mapper.Map<FooDerived1>(dto)
           : Mapper.Map<FooDerived2>(dto));

This would map the Foo property of FooContainer to the correct derived type of FooBase.

But how can I do this without the static API? The IMapper instance is not yet created at the point of configuring the profile. Is there a way to leverage the overload of ConvertUsing() which takes a Func< ResolutionContext,object >? Can the resolution context give me whatever IMapper is currently being used? I've been looking, but can't find anything usable.

1
So, funnily enough I've been using a bit of a temporary hack for this. I have my own TypeConverter class which contains IMapper _defaultEngine = MyGlobalConfig.Engine. I then create two configurations, one using the static global, and another which I place in my own static location and use as a "fallback" to simpler mappings.Thomas Boby
That probably works, but 'hack' is the right name alright :) I did a little hack myself to make it work; instead of injecting the IMapper itself into the DI, I inject an instance of MapperContainer which has a property to hold the actual IMapper instance. I can then inject the MapperContainer into my Profile and set up the ConvertUsing-lambdas to access the mapper property even though it will be NULL up until the bootstrapper has actually set the IMapper instance.Julian Hanssen
Actually, looking at your problem again, why can't you just create a TypeConverter and use ResolutionContext.Engine.Map inside your Convert?Thomas Boby
ResolutionContext.Engine.Map takes a ResolutionContext as parameter. How can i create a resolutioncontext for different destinationtype? Am I missing something?Julian Hanssen

1 Answers

2
votes

One way to get access to the mapping engine is via your own TypeConverter

abstract class MyTypeConverter<TSource,TDestination> : ITypeConverter<TSource, TDestination>
{
    protected ResolutionContext context;
    public TDestination Convert(ResolutionContext context)
    {
        this.context = context;
        return Convert((TSource)context.SourceValue);
    }
    public abstract TDestination Convert(TSource source);
}

You then create an actual implementation like:

class MyTypeMapper : MyTypeConverter<EnumType,EnumTypeView>
{
    public override EnumTypeView Convert(EnumType source)
    {
        return context.Engine.Mapper.Map<EnumTypeID, EnumTypeView>(source.EnumBaseType);
    }
}

Except instead of unwrapping an enum structure, you'd check the type and call Map with different types.