Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapper vs Constructor

Tags:

c#

automapper

I am working on a project that was setup with AutoMapper. Since most of this is foreign to me, I am keeping an open mind as to why.

Let's say I have two classes:

class Foo
{
    public string name { get; set; }
    public int rank { get; set; }
    public bool isDelected { get; set; }
}

class FooView
{
    public string name { get; set; }
    public int rank { get; set; }
}

instead of using an ORM, why wouldn't I just use a constructor which takes FooView as a parameter.

class Foo
{
    public Foo(FooView source)
    {
        this.name = source.name;
        this.rank = source.rank;
    }
}

In this case whenever I have a FooView and I need Foo I just say

var myFoo = new Foo(FooView);

It give me all the logic I need in one location and I can use it everywhere. If I need to update the code it is same place as the Model definition. So what am I missing?

like image 249
Infinidem Avatar asked Jul 05 '16 18:07

Infinidem


People also ask

Does AutoMapper call constructor?

At the time of writing this answer, AutoMapper will do this automatically (with a simple CreateMap<>() call) for you if the properties match the constructor parameters.

Why not use AutoMapper?

1. If you use the convention-based mapping and a property is later renamed that becomes a runtime error and a common source of annoying bugs. 2. If you don't use convention-based mapping (ie you explicitly map each property) then you are just using automapper to do your projection, which is unnecessary complexity.

What is the use of mapper in Java?

ObjectMapper class ObjectMapper provides functionality for reading and writing JSON, either to and from basic POJOs (Plain Old Java Objects), or to and from a general-purpose JSON Tree Model (JsonNode), as well as related functionality for performing conversions.

What is the use of mapper class?

It acts as a mapper between two objects and transforms one object type into another. It converts the input object of one type to the output object of another type until the latter type follows or maintains the conventions of AutoMapper.


2 Answers

This may be fine if all of your models are in the same assembly or have a direct dependency on the other items. AutoMapper is great if your models align nicely, but if you need a bit more control over the mapping I tend to put these in a static mapping method in a dedicated helper or within the MVC Controller.

This helps keep my mapping logic separate from the models

private static Foo MapToFoo(FooView vm) 
{
    var foo = new Foo();
    foo.Name = vm.Name;
    foo.Rank = vm.Rank;
    ...
    return foo;
}
like image 74
Steve Avatar answered Oct 02 '22 13:10

Steve


In general, I like the pattern you're following. Even though it makes me somewhat of a heretic, I prefer to manually map everything. For example, database pocos and further transforming database pocos to view models. Yes, you could make the argument about certain classes having an abundance of properties. But maybe that's a code smell?

If your project happens to have a ton of these legitimate classes with 20-30 properties, then perhaps you should take a quick break, teach yourself PowerShell and start generating the mappings.

So why go through all this effort? It's simple, reflection is expensive and it sucks. It loves resources, and is guaranteed to make your code slow despite whatever efforts you make caching etc when things scale even moderately. It's also more difficult to debug.

I'm just simply not in love with handing over the decision making, of perhaps the most brittle part of my code, over to the runtime.

Now on to the constructor vs mapper debate. For a small project, I would reckon that constructor's will work perfectly. It's easy to understand and test. As the project grows, mapper's become a nice touch because they allow for a separation of concerns. But as is always the case, don't immediately reach for a library, think about the problem your faced with and try and solve it with code.

In our case, we're looking to project one thing into another, so let's define a simple interface for that:

public interface IMapper<in TSource, in TResult>
    where TResult : new() {
        void Map(TSource source, TResult result);
}

This describes a generic interface with two input types TSource and TResult, the latter having a new() constraint so we can manufacture it programmatically.

Now let's add some functionality, for mapping one-to-one, one-to-one with nulls and many-to-many:

public static class IMapperExtensions {
    public static TResult Map<TSource, TResult>(this IMapper<TSource, TResult> mapper, TSource source)
        where TResult : new() {
        TResult result = new TResult();

        mapper.Map(source, result);

        return result;
    }

    public static TResult MapAllowNull<TSource, TResult>(this IMapper<TSource, TResult> mapper, TSource source)
        where TResult : new() {
        if (source == null) {
            return default(TResult);
        }

        return mapper.Map(source);
    }

    public static IEnumerable<TResult> MapCollection<TSource, TResult>(this IMapper<TSource, TResult> mapper, IEnumerable<TSource> source)
        where TResult : new() {
        if (source == null) {
            yield return default(TResult);
        }
        else {
            foreach (var s in source) {
                yield return mapper.Map(s);
            }
        }
    }
}

An the implementation:

class Foo {
    public string name { get; set; }
    public int rank { get; set; }
    public bool isDelected { get; set; }
}

class FooView {
    public string name { get; set; }
    public int rank { get; set; }
}

class FooToFooView : IMapper<Foo, FooView> {
    public void Map(Foo source, FooView result) {
        result.name = source.name;
        result.rank = source.rank
    }
}

Whatever you do, resist the urge to define a static method for this. Elect either the constructor, or the mapper.

like image 37
pim Avatar answered Oct 02 '22 14:10

pim