AutoMapper/AutoMapper.Extensions.ExpressionMapping

Cannot convert expressions that are for a struct property to non-struct properties

yinzara opened this issue · 3 comments

This defect seems to have been introduced in the v12 release.

I think it might actually be caused by:
#144

When attempting to map expressions between properties that are convertible types but one type is a struct and the other is a class, an exception is thrown (instead of the mapping being valid).

ystem.InvalidOperationException : No coercion operator is defined between types 'MyStructType' and 'MyClassType'.
at System.Linq.Expressions.Expression.GetUserDefinedCoercionOrThrow(ExpressionType coercionType, Expression expression, Type convertToType)
   at System.Linq.Expressions.Expression.Convert(Expression expression, Type type, MethodInfo method)
   at System.Linq.Expressions.Expression.Convert(Expression expression, Type type)
   at AutoMapper.Extensions.ExpressionMapping.Extensions.VisitorExtensions.ConvertTypeIfNecessary(Expression expression, Type memberType)
   at AutoMapper.Extensions.ExpressionMapping.XpressionMapperVisitor.<VisitMember>g__GetMappedMemberExpression|17_0(Expression parentExpression, List`1 propertyMapInfoList, <>c__DisplayClass17_0& )
   at AutoMapper.Extensions.ExpressionMapping.XpressionMapperVisitor.VisitMember(MemberExpression node)
   at System.Linq.Expressions.MemberExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at AutoMapper.Extensions.ExpressionMapping.MapperExtensions.<MapExpression>g__MapBody|8_1[TDestDelegate](Dictionary`2 typeMappings, XpressionMapperVisitor visitor, <>c__DisplayClass8_0`1& )
   at AutoMapper.Extensions.ExpressionMapping.MapperExtensions.<MapExpression>g__CreateVisitor|8_0[TDestDelegate](Dictionary`2 typeMappings, <>c__DisplayClass8_0`1& )
   at AutoMapper.Extensions.ExpressionMapping.MapperExtensions.MapExpression[TDestDelegate](IConfigurationProvider configurationProvider, LambdaExpression expression, Type typeSourceFunc, Type typeDestFunc, Func`3 getVisitor, Func`2 shouldConvertMappedBodyToDestType)
   at AutoMapper.Extensions.ExpressionMapping.MapperExtensions.MapExpression[TDestDelegate](IMapper mapper, LambdaExpression expression, Func`3 getVisitor, Func`2 shouldConvertMappedBodyToDestType)
   at AutoMapper.Extensions.ExpressionMapping.MapperExtensions.MapExpression[TDestDelegate](IMapper mapper, LambdaExpression expression)

I have created a simple reproduction repository:
https://github.com/yinzara/AutoMapper.Extensions.ExpressionMapping.bug-mapped-types

The expression mapper supports ForMember, ForPath and property maps. It does not support ConverUsing. It also treats literals differently as you can see from #144 .

I think you may have hastily closed this before fully understanding it. The issue seems to be with structs specifically, not with ConvertUsing.

I've updated the example with more specifics that don't always use ConvertUsing yet the bug still exists.

My test has the following cases of mapping property expressions from one entity to another:

Working Properly:
Map_Date_ToDate2 - object property to another object property that are convertible using CreateMap (not ConvertUsing) -> Succeed
Map_DateOnly_ToString - struct property to string property that are convertible using ConvertUsing -> Fails
Map_string_ToDate - string property to object property that are convertible using ConvertUsing -> Fails
Map_string_ToDateOnly - string property to struct property that are conertible using ConvertUsing -> Fails

Not Working as you describe:
Map_DateOnly_ToDate - struct property to another object property that are convertible using CreateMap (not ConvertUsing) -> Fails - this should succeed according to your above statement
Map_Date_ToString - object property to string property that are convertible using ConvertUsing -> Succeed - this should fail according to your above statement

Like I said this library treats literals differently. You can change these structures back and forth between struct and class without expression mapping errors for example. You'll find the tests use ForMember rather than CreateMapto convert literal types.

Consider trying to reproduce your failing case without literal types, ConvertUsings etc..