azrael.pl azrael.pl - 15 days ago 11
C# Question

C# Creating AutoMapper mapping using reflection

I need to create a mapping (using AutoMapper) from n classes all being derived from one abstract class to a contract class

So for example:

public abstract class bar
{
public string Field1 {get; set;}
public someClass Field2 {get; set;}
}

public class foo1bar: bar
{
// members
}

public class foo2bar: bar
{
// members
}

public class barContract
{
public string Field1 {get; set;}

// this will use existing someClass.Description field
public string Field2Description {get; set;}
}


implementations of bar class are multiple, and also are likely to change (more will be added). As Automapper cannot map to abstract class (so the constructor
mapperConfiguration.CreateMap<bar, barContract>()
is incorrect), I was wondering will it be possible to use reflection to find all classes 'implementing' bar class and map them 'automatically'

var type = typeof(bar);
var types = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(p => type.IsAssignableFrom(p));


I've got the Types, and I'm trying to invoke CreateMap.
As the type is now a variable, I'm creating a generic method once again using reflection:

foreach (Type t in types)
{
mapperConfiguration.GetType().GetMethod("CreateMap")
.MakeGenericMethod(t, typeof(barContract))
.Invoke(mapperConfiguration, null);
}


The problem is that CreateMap is not a member of type that is extracted from mapperConfiguration instance - when I'm trying to extract the method by name I get null. I see it's defined in IProfileExpression, so I'm trying to extract the method from the interface:
typeof(IProfileExpression).GetMethod("CreateMap")
and I get System.Reflection.AmbiguousMatchException - what is kind of OK, but using System.Reflection.BindingFlags in GetMethod to be more specific I'm again getting nulls.

What am I doing wrong, or how to get around that mapping problem ?

Win Win
Answer

You can create map from one type to another type CreateMap(SouceType, DestinationType));

public abstract class Bar
{
    public string Field1 { get; set; }
}

public class Foo1bar : Bar
{
    // members
}

public class Foo2bar : Bar
{
    // members
}

public class BarContract
{
    public string Field1 { get; set; }

    // this will use existing someClass.Description field
    public string Field2Description { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        AutoMapperConfiguration.Init();

        var foo1 = new Foo1bar {Field1 = "One"};
        var barContract1=AutoMapperConfiguration.Mapper.Map<Foo1bar, BarContract>(foo1);
        Console.WriteLine("barContract1.Field1: " + barContract1.Field1);

        var foo2 = new Foo2bar {Field1 = "Two"};
        var barContract2=AutoMapperConfiguration.Mapper.Map<Foo2bar, BarContract>(foo2);
        Console.WriteLine("barContract2.Field1: " + barContract2.Field1);

        Console.ReadLine();
    }

    public static class AutoMapperConfiguration
    {
        public static void Init()
        {
            MapperConfiguration = new MapperConfiguration(cfg =>
            {
                var types = Assembly.GetExecutingAssembly().GetTypes()
                    .Where(type => !string.IsNullOrEmpty(type.Namespace) &&
                                    type.BaseType != null &&
                                    type.BaseType == typeof(Bar));
                foreach (Type type in types)
                {
                    cfg.CreateMap(type, typeof(BarContract));
                }
            });

            Mapper = MapperConfiguration.CreateMapper();
        }

        public static IMapper Mapper { get; private set; }

        public static MapperConfiguration MapperConfiguration { get; private set; }
    }
}

Output

enter image description here