fluentsprings / ExpressMapper

Mapping .Net types
http://www.expressmapper.org
Other
310 stars 65 forks source link

Remove some redundancy when mapping #116

Open mdarefull opened 8 years ago

mdarefull commented 8 years ago

Some common characteristic I've seen on several mappers is that they are too verbose and very redundant. And actually, it has been the main reason behind how I found ExpressMapper, I was trying to look for some mapper easier to write, to read, less redundant.

For example, let's take this registration code: Mapper.Register<Product, ProductViewModel>();

To perform a mapping from IEnumerable to IEnumerable, one nice way is: var viewModels = products.Map<IEnumerable<Product>, IEnumerable<ProductViewModel>>(); As you can see, it is somewhat redundant, because we need to specify the source type and the destination type. Furthermore, if the source type is a collection, there's no point on specifying the destination type as a non-collection object.

What about this approach for the above example? var viewModels = products.Map(typeof(ProductViewModel))

In this case, I'm proposing several things:

  1. The extension method takes the type of the destination as a parameter, instead than a type parameter.
  2. The extension method defaults to IEnumerable when mapping collections. After all, this is the approach used by the extension methods of LINQ and everybody loves it!
mdarefull commented 8 years ago

Also, and I don't know if it's possible, an even better signature from the readability point of view (at least IMO) for the extension method is: public static TN Map<TN>(this object source);

After all, if it works having the destination type as argument, we can get the actual type of source: var soureType = source.GetType();

I don't know the exact implementation details of your approach, sorry if I'm proposing non-senses. I just think that this approach would be a killer feature from the readability perspective, a very clean way to map objects, something I haven't seen before.

nickcoad commented 8 years ago

@mdarefull in the case of your example, instead of writing:

var viewModels = products.Map<IEnumerable<Product>, IEnumerable<ProductViewModel>>();

You can simply use the underlying types:

var viewModels = products.Map<Product, ProductViewModel>();

So I don't think anything needs to change.

I also disagree that the signature of:

public static TN Map<TN>(this object source);

Is more readable than the existing signature of:

public static TN Map<T, TN>(this T source)
{
    return Mapper.Map<T, TN>(source);
}

There are plenty of other reasons as to why the second approach is preferred, but that's for another day :)

mdarefull commented 8 years ago

I still think that: var viewModels = products.Map<Product, ProductViewModel>(); is too redundant. But you have a point, it won't be a ground-breaker neither, it just feel weird having to specify the source type and keeps me awake at nights :)

For the second suggestion, yes, the method signature may be odd, but is its usage I'm referring to, after all, once you get used to it you'll never think about its signature again, but you'll be seeing all around your project something like: var viewModels = Mapper.Map<ProductViewModel>(products); Or even better: var viewModels = products.Map().To<ProductViewModel>(); Just try to read it aloud. Well, I think I get too passionate and academic with this last one :)

nickcoad commented 8 years ago

My apologies @mdarefull I misunderstood what you were referring to. Yes the redundancy is confusing, especially coming from AutoMapper, but I'm not sure if that's just because I'm used to AutoMapper!

anisimovyuriy commented 8 years ago

Thanks a lot @mdarefull! - Let me see what we can do with that as I agree with you.

mdarefull commented 8 years ago

@nickcoad I was checking your proposal of just using the underlying types: var viewModels = products.Map<Product, ProductViewModel>();

And I think it is invalid, that's an extension method, so, the first type parameter should be the type of products which is IEnumerable and as a consequence you're calling an extension method of Product on an IEnumerable which confuses my compiler. I hope I'm wrong because I liked the idea, but I think it is a valid thing to mention.