Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mapping and Projecting enum strange behavior #840

Closed
brenovieira opened this issue Aug 19, 2015 · 6 comments
Closed

Mapping and Projecting enum strange behavior #840

brenovieira opened this issue Aug 19, 2015 · 6 comments
Milestone

Comments

@brenovieira
Copy link

It's quite a question father than an issue, because I don't know the right behavior. I'm using AutoMapper 4.0.4.

I'm trying to map and project (ProjectTo<>) some enum properties and the only working configuration I found was something like:

Mapper.CreateMap<Source, Destination>();
var m = Mapper.CreateMap<SourceType, DestinationType>();
m.ProjectUsing(s => (DestinationType)s);
m.ConvertUsing(s => (DestinationType)s);

//Source and Destination are classes
//SourceType and DestinationType are enums and properties of Source and Destination
  • Using Mapper.Initialize(cfg => ...) or Mapper.AddProfile<Profile>() have the same behavior

Is there another way (simpler) to achieve that (mapping and projecting enums)?
I'm asking this because I have several enums and if I just map them, there is no need of creating enum mapping, but projecting them doesn't work "automatically".

Please see a gist I created here: https://gist.github.com/brenovieira/106b904f3899713e090d

@jbogard
Copy link
Member

jbogard commented Aug 20, 2015

Well, to use projection, the LINQ provider needs to understand that ProjectUsing piece. The trick is to find the correct expression that EF I'm assuming will understand.

What behavior are you seeing?

@brenovieira
Copy link
Author

The behavior I didn't except is based on 2 points:

  1. To map an enum SourceType to another enum DestinationType, I don't need to call CreateMap<SourceType, DestinationType>(). AutoMapper "knows" this mapping by default.
    You said that long time ago at StackOverflow, so I didn't created it at first.
    In case of mapping a class Sourceto another class Destination, we must call CreateMap<Source, Destination>().
    But to use projection the behavior is the opposite:
    To enums, I must call CreateMap<SourceType, DestinationType>().ProjectUsing(s => ...), but to project classes I don't need to call .ProjectUsing(s => ...), only CreateMap<Source, Destination>() works.
  2. To just map, I can configure AutoMapper like this:
Mapper.CreateMap<Source, Destination>();

And both mappings work: Mapper.Map<Source, Destination>(source);and Mapper.Map<SourceType, DestinationType>(sourceType);.

To just project, as I said in 1., I can configure it like this:

Mapper.CreateMap<Source, Destination>();
Mapper.CreateMap<SourceType, DestinationType>().ProjectUsing(s => (DestinationType)s);

And both projections work: (IQueryable<Source>).ProjectTo<Destination>(); and (IQueryable<SourceType>).ProjectTo<DestinationType>();.

The problem happens when I wanna map and project, because the configuration above doesn't work, so I need to configure it differently:

Mapper.CreateMap<Source, Destination>();
var m = Mapper.CreateMap<SourceType, DestinationType>();
m.ProjectUsing(s => (DestinationType)s);
m.ConvertUsing(s => (DestinationType)s);

In other words, I don't need to create enum mapping, but if I create it (to project, for instance), I also need to call another method (.ConvertUsing(...)) to tell AutoMapper how to map enum.

The gist I created has unit tests of the 3 use cases above.
What do you think, @jbogard ?

I believe that if it is the expected behavior, it should be explained in documentation at least. I searched on, but I found nothing about enum specifically. So I spent some time trying different configurations to find a working one :( hahaha

@jbogard
Copy link
Member

jbogard commented Aug 20, 2015

AutoMapper has two modes, projection and mapping. Projection uses only
expressions to map, and mapping can do waaaaaaaay more things. Though your
two code snippets look the same, one uses Func and one uses Expression. The
piece that executes the mapping are also completely separate. The
projection doesn't actually map anything, it creates an expression tree for
some instructions to map, then the LINQ provider does the actual mapping.

Hence the difference.

If you want to map and project with low level mappings, you need to use
both HOWEVER I could see ProjectUsing only being needed for simple cases.

On Wednesday, August 19, 2015, Breno Vieira notifications@github.com
wrote:

The behavior I didn't except is based on 2 points:

To map an enum SourceType to another enum DestinationType, I don't
need to call CreateMap<SourceType, DestinationType>(). AutoMapper
"knows" this mapping by default.
You said that long time ago at StackOverflow
http://stackoverflow.com/questions/11265829/how-can-i-map-between-two-enums-using-automapper/11268711#11268711,
so I didn't created it at first.
In case of mapping a class Sourceto another class Destination, we must
call CreateMap<Source, Destination>().
But to use projection the behavior is the opposite:
To enums, I must call CreateMap<SourceType,
DestinationType>().ProjectUsing(s => ...), but to project classes I
don't need to call .ProjectUsing(s => ...), only CreateMap<Source,
Destination>() works.
2.

To just map, I can configure AutoMapper like this:

Mapper.CreateMap<Source, Destination>();

And both mappings work: Mapper.Map<Source, Destination>(source);and Mapper.Map<SourceType,
DestinationType>(sourceType);.

To just project, as I said in 1., I can configure it like this:

Mapper.CreateMap<Source, Destination>();
Mapper.CreateMap<SourceType, DestinationType>().ProjectUsing(s => (DestinationType)s);

And both projections work: (IQueryable).ProjectTo();
and (IQueryable).ProjectTo();.

The problem happens when I wanna map and project, because the
configuration above doesn't work, so I need to configure it differently:

Mapper.CreateMap<Source, Destination>();
var m = Mapper.CreateMap<SourceType, DestinationType>();
m.ProjectUsing(s => (DestinationType)s);
m.ConvertUsing(s => (DestinationType)s);

In other words, I don't need to create enum mapping, but if I create it
(to project, for instance), I also need to call another method
(.ConvertUsing(...)) to tell AutoMapper how to map enum.

The gist I created has unit tests of the 3 use cases above.
What do you think, @jbogard https://github.com/jbogard ?

I believe that if it is the expected behavior, it should be explained in
documentation at least. I searched on, but I found nothing about enum
specifically. So I spent some time trying different configurations to find
a working one :( hahaha


Reply to this email directly or view it on GitHub
#840 (comment)
.

@brenovieira
Copy link
Author

@jbogard, I'm sorry for the delay.

AutoMapper has two modes, projection and mapping. Projection uses only expressions to map, and mapping can do waaaaaaaay more things. Though your two code snippets look the same, one uses Func and one uses Expression.

That's absolutelly correct.
Indeed, as I have like 30 enums, I made this method to create my enum mappings:

protected void CreateEnumMap<TEnum1, TEnum2>(Expression<System.Func<TEnum1, TEnum2>> exp,
    Expression<System.Func<TEnum2, TEnum1>> reverseExp, bool createReverse = true)
{
    var map = CreateMap<TEnum1, TEnum2>();
    map.ConvertUsing(exp.Compile());
    map.ProjectUsing(exp);

    if (createReverse) CreateEnumMap(reverseExp, exp, false);
}

The piece that executes the mapping are also completely separate. The projection doesn't actually map anything

That I'm not sure. I didn't dig into this part of the AutoMapper code, but as an AutoMapper user perspective I see that:

Mapper.CreateMap<Source, Destination>();
Mapper.Map<Source, Destination>(source); //this works
Mapper.CreateMap<Source, Destination>();
Mapper.CreateMap<SourceType, DestinationType>().ProjectUsing(s => (DestinationType)s);
Mapper.Map<Source, Destination>(source); //this doesn't work correctly

What I'm suggesting is AutoMapper creates the expression tree to enum-enum projections.
Then creating enum-enum mapping wouldn't be necessary to map and project.

In my large AutoMapper config, I just call .ProjectUsing() and .ConvertUsing() with enums.
That's a problem to maintenance, because I need treat class and enum separetely.

I map and project two POCO classes (with same property names), just callling CreateMap<Source, Destination>().

On the other hand, to enum I don't need to call CreateMap<> to use mapping.
But if I project it, I need to call CreateMap<> and ProjectUsing
Even worse, If I map and project it, I need to call CreateMap<>, ProjectUsing and ConvertUsing.

It's not a clear behavior to me and the documentation doesn't explain that.
Do you agree? hehe

@jbogard
Copy link
Member

jbogard commented Aug 21, 2015

Yeah, in fact MapFrom does work on both sides which kinda invalidated my argument.

@lock
Copy link

lock bot commented May 8, 2019

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@lock lock bot locked as resolved and limited conversation to collaborators May 8, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants