AutoMapper/AutoMapper.Extensions.ExpressionMapping

No coercion operator is defined between types 'Entity' and 'Dto'

Closed this issue · 7 comments

There are two types: Entity and Dto. They are identical mapped CreateMap<Entity, Dto>().ReverseMap();

Expression<Func<Dto, Dto>> selector = c => new Dto{}; var resEx = mapper.MapExpression<Expression<Func<Entity, Dto>>>(selector);

In AutoMapper.Extensions.ExpressionMapping v3.0.6 that works fine, but in latest v3.1.0 fails with error:
No coercion operator is defined between types 'Entity' and 'Dto'

There was a bug, but what you were doing in 3.0.6 will probably needs a different approach. An improvement to map member initialization like the following was implemented in 3.1.0.

        [Fact]
        public void Map_MemberInit()
        {
            //Arrange
            Expression<Func<ThingModel, ThingModel>> selection = s => new ThingModel { Car = s.Car };

            //Act
            Expression<Func<Thing, Thing>> selectionMapped = mapper.MapExpression<Expression<Func<Thing, Thing>>>(selection);

            //Assert
            Assert.NotNull(selectionMapped);
        }

Mapping empty constructors in expression was not implemented though.

        [Fact]
        public void Map_Constructor_NoParams()
        {
            //Arrange
            Expression<Func<ThingModel, ThingModel>> selection = s => new ThingModel();

            //Act
            Expression<Func<Thing, Thing>> selectionMapped = mapper.MapExpression<Expression<Func<Thing, Thing>>>(selection);

            //Assert
            Assert.NotNull(selectionMapped);
        }

Then there's what you were doing which was to map the parameter and leave the same type unmapped in the body. It worked before because the feature was missing if you like.

        Expression<Func<Dto, Dto>> selector = c => new Dto{};
        var resEx = mapper.MapExpression<Expression<Func<Entity, Dto>>>(selector);

We shouldn't purposefully design selectively mapping of parts of the expression.
Please try version 3.1.1-preview01. The exception should go away but both parameter and body will be mapped.

@BlaiseD thank you for quick reply. But in general I was using this feature when I map selectively types, that gave me possibility to select directly from Entity to Dto and decouple my logic layer from data access layer. Not sure how should I write select expressions in logic layer and map them directly to Dto output type now. In v3.0.6 that selective mapping worked just fine.

The way it worked in 3.0.6 was bug.

If it was to be a feature then take the following expression.
Expression<Func<UserModel, IEnumerable<ThingModel>>> selection = s => s.AccountModel.ThingModels.Select(x => new ThingModel { Car = x.Car }).Where(b => b.Color.EndsWith("e"));

We'd have to build in logic to specify which of UserModel, ThingModel, AccountModel or CarModel should be mapped. Maybe consider how you would implement that in a PR.

Sounds like what you're looking for in ProjectTo as implemented here.

@BlaiseD ProjectTo does not give control over navigation properties. If I have 10 nav properties in my DTO they all will be populated. But if I want 5 of them in one query and another 5 in another query - I will have to keep separate DTOs for every such request. And 10 navigation properties can have hundreds of possible combinations, which of them I want to expand during query.

Plenty of examples of ProjectTo using explicit expansion.

That should work.

@BlaiseD ok thank you, I think I can manage to work with latest version of MapExpression. But in general, ProjectTo does not give possibility to expand sub-collections https://stackoverflow.com/questions/60225634/automapper-projectto-with-dbcontext-multi-level-include
So, in general if I want to use projection select => new MayDTO {Prop1 = select.Prop1} and multi-level include (expand) then ProjectTo does not work here. However it's quite possible that I'm missing something,

Yes, I believe that is correct for version 9.0 but has been resolved in the repository. The myget build should work for expanding sub collections.