0

For performance reasons I cannot use Automapper or any other convention based mapping tool. Ideally I would like one method signature that we can call... e.g. - mapper.Map<,>() however the following isn't valid syntax

   public MyModelView Map<Tin, Tout>(Tin source)
        where Tin: MyModelDto 
        where Tout: MyModelView 
    {
        return new MyModelView{
             Prop1 = source.Prop1;
        };
    }

    public MyModelDto Map<Tin, Tout>(Tin source)
        where Tin: MyModelView 
        where Tout: MyModelDto 
    {
        return new MyModelDto {
             Prop1 = source.Prop1;
        };
    }

One solution that works but I personally find ugly is the following.

    public void Map(MyModelDto source, out MyModelView outParam)
    {
        outParam = new MyModelView();
        outParam.Prop1 = source.Prop1;
    }

    public void Map(MyModelView source, out MyModelDto outParam)
    {
        outParam = new MyModelDto ();
        outParam.Prop1 = source.Prop1;
    }

    MyModelDto myDto = new MyModelDto { Prop1 = "Hello World" };
    MyModelView myView;
    mapper.Map(myDto, out myView);

My question is, is there any way to force a definite type without using reflection or an out param to force uniqueness or is the out param method the cleanest approach I'm going to get?

UPDATE: So I have made a solution that satisfies my need, it utilizes the RuntimeTypeHandle which is not completely ideal but it works for me and causes the least dev pain for use. If my requirements only had allowed for 1 type conversion per object I would have stuck with a simpler solution. For completeness...here is what I've come up with.

credit for the type handling idea: What is quicker, switch on string or elseif on type?

public class Mapper : IMapper
{
    private delegate object TypeHandler(Object node, Type desiredReturn);

    private static readonly Dictionary<RuntimeTypeHandle, TypeHandler> TypeLibrary = CreateTypeHandler();

    private static Dictionary<RuntimeTypeHandle, TypeHandler> CreateTypeHandler()
    {
        var ret = new Dictionary<RuntimeTypeHandle, TypeHandler>();

        ret[typeof(MyModelDto).TypeHandle] = HandleMyModelDto;
        //ret[typeof (Jill).TypeHandle] = HandleJill;
        //ret[typeof (Marko).TypeHandle] = HandleMarko;
        return ret;
    }

    private static object HandleMyModelDto(object source, Type desiredMapping)
    {
        MyModelDto sourceObj = source as MyModelDto;

        if (desiredMapping == typeof(MyModelView))
        {
            return new MyModelView { Prop1 = sourceObj.Prop1 };
        }
        else if (desiredMapping == typeof (MyModelBusiness))
        {
            return new MyModelBusiness { Prop1 = sourceObj.Prop1 };
        }
    }

    public Tout Map<Tin, Tout>(Tin source)
    {
        TypeHandler handler;
        if (TypeLibrary.TryGetValue(Type.GetTypeHandle(source), out handler))
        {
            return (Tout)handler(source, typeof(Tout));
        }
        else
        {
            //Unexpected type...
            throw new NotImplementedException("Type mapping not implemented");
        }
    }
}

and consumption is as follows

Mapper mapper = new Mapper();
MyModelDto myDto = new MyModelDto { Prop1 = "Hello World" };
MyModelView returnVal = mapper.Map<MyModelDto, MyModelView>(myDto);
Community
  • 1
  • 1
Jon Gear
  • 1,008
  • 1
  • 11
  • 22
  • 1
    `For performance reasons I cannot use Automapper or any other convention based mapping tool.` Have you measured the performance impact vs. your own custom mapping solution? – Eric J. Oct 21 '15 at 17:16
  • Yup, the performance gain has been immense – Jon Gear Oct 21 '15 at 17:28
  • 1
    Why does you overloaded version use `out` parameters instead of just returning the value? Are there multiple output types? – Lee Oct 21 '15 at 17:32
  • It could just return but then I would still need to pass in an object of the second param type. It seemed like the only use for a second parameter that I'm being forced to use by c# since it doesn't value return types as part of a unique method signature – Jon Gear Oct 21 '15 at 17:36

2 Answers2

1

Unfortunately, in C#, you can't define two methods with the same signature (i.e. same method name + parameters) to return two different types, however, if your view and dto objects implement a common interface, for example, something like the following:

public interface IMyModel
{
    string Prop1 { get; set; }
}

You can then define your Map method as follows:

public Tout Map<Tin, Tout>(Tin source)
    where Tin: IMyModel 
    where Tout: IMyModel, new() 
{
    var result = new Tout();
    result.Prop1 = source.Prop1;
    return result;
}

And then be able to call it as:

var view = Map<MyModelDto, MyModelView>(dtoInstance);
var dto = Map<MyModelView, MyModelDto>(viewInstance);
Arca Artem
  • 1,063
  • 1
  • 10
  • 20
0

Why are you trying to make Map generic if you know the types and members involved? Forget Tin and Tout and just use the base types that you're accepting/returning.

public MyModelView Map(MyModelDto source)
{
    return new MyModelView{
         Prop1 = source.Prop1
    };
}

Anything based on MyModelDto must have a Prop1 and anything based on MyModelView must have a Prop1 too. Even if it's a subclass of either one, if you're to make use of it without reflection those members must be defined in the base classes. If you have variations per subclass, like extra members, then you need to create custom versions of the mapper functions for each legit transformation (and NOT use class inheritance for this purpose, really, because it only adds complication and no benefit here). It is going to be the fastest code, at the cost of maintenance of the mapping routines.

This only works if you only need to map MyModelDto to one other object type. Say I want to save the dto to my db object tier or to a different view model. I won't be able to make a unique method signature since C# doesn't consider return types as part of a unique method signature

Then you can:

public void Map(MyModelDto source, out MyModelView target)
{
    target = new MyModelView {
         Prop1 = source.Prop1
    };
}

But I'd rather suggest that you don't do this because working with out params can be annoying. Instead, use MapToView, MapToModel etc or keep Map but differentiate them by different namespaces (for an extension methods approach - possibly the best option) or static class name (doing MapView.Convert() and MapModel.Convert() or something). Namespaces is probably the best strategy because it seems likely to me that in most sections of code, you'll only be dealing with one version OR the other, not both.

Jason Kleban
  • 20,024
  • 18
  • 75
  • 125
  • This only works if you only need to map MyModelDto to one other object type. Say I want to save the dto to my db object tier or to a different view model. I won't be able to make a unique method signature since C# doesn't consider return types as part of a unique method signature – Jon Gear Oct 21 '15 at 17:47