From 14b36267391e3ac60fd68463d39ac9114f847255 Mon Sep 17 00:00:00 2001 From: ErikGjers Date: Sat, 11 Feb 2023 12:41:47 +0100 Subject: [PATCH 1/5] Added enum to int conversion for unmapped side of binary expressions --- .../XpressionMapperVisitor.cs | 11 ++ .../ExpressionMappingEnumToIntOrString.cs | 131 ++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToIntOrString.cs diff --git a/src/AutoMapper.Extensions.ExpressionMapping/XpressionMapperVisitor.cs b/src/AutoMapper.Extensions.ExpressionMapping/XpressionMapperVisitor.cs index ff354d7..c9970f0 100644 --- a/src/AutoMapper.Extensions.ExpressionMapping/XpressionMapperVisitor.cs +++ b/src/AutoMapper.Extensions.ExpressionMapping/XpressionMapperVisitor.cs @@ -431,6 +431,17 @@ Expression DoVisitBinary(Expression newLeft, Expression newRight, Expression con { if (newLeft != node.Left || newRight != node.Right || conversion != node.Conversion) { + if(node.Left.Type.IsEnum && node.Right.Type.IsEnum) + { + if (newLeft.Type.IsEnum && !newRight.Type.IsEnum) + { + newLeft = Expression.Convert(newLeft, newRight.Type); + } + else if (!newLeft.Type.IsEnum && newRight.Type.IsEnum) + { + newRight = Expression.Convert(newRight, newLeft.Type); + } + } if (node.NodeType == ExpressionType.Coalesce && node.Conversion != null) return Expression.Coalesce(newLeft, newRight, conversion as LambdaExpression); else diff --git a/tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToIntOrString.cs b/tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToIntOrString.cs new file mode 100644 index 0000000..924e376 --- /dev/null +++ b/tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToIntOrString.cs @@ -0,0 +1,131 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Linq.Expressions; +using Xunit; + +namespace AutoMapper.Extensions.ExpressionMapping.UnitTests +{ + public class ExpressionMappingEnumToIntOrString : AutoMapperSpecBase + { + public enum SimpleEnum + { + Value1 = 1, + Value2 = 2, + Value3 = 3 + } + private class EntityDto + { + internal SimpleEnum Value { get; set; } + } + private class EntityAsString + { + internal string Value { get; set; } + } + private class EntityAsInt + { + internal int Value { get; set; } + } + public class SimpleEnumToStringTypeResolver : ITypeConverter + { + public string Convert(SimpleEnum source, string destination, ResolutionContext context) + { + return source.ToString(); + } + } + protected override MapperConfiguration Configuration + { + get + { + return new MapperConfiguration(config => + { + config.AddExpressionMapping(); + config.CreateMap() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)); + config.CreateMap() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)); + config.CreateMap().ConvertUsing(); + }); + } + } + + [Fact] + public void BinaryExpressionOfEnumsToInt() + { + Expression> mappedExpression; + { + var param = Expression.Parameter(typeof(EntityDto), "x"); + var property = Expression.Property(param, nameof(EntityDto.Value)); + var constant = Expression.Constant(SimpleEnum.Value2, typeof(SimpleEnum)); + var binaryExpression = Expression.Equal(property, constant); + var lambdaExpression = Expression.Lambda(binaryExpression, param); + mappedExpression = Mapper.Map>>(lambdaExpression); + } + + Expression> translatedExpression= translatedExpression = x => x.Value == 2; + + var mappedExpressionDelegate = mappedExpression.Compile(); + var translatedExpressionDelegate = translatedExpression.Compile(); + + var entity = new EntityAsInt { Value = (int)SimpleEnum.Value2 }; + var mappedResult = mappedExpressionDelegate(entity); + var translatedResult = translatedExpressionDelegate(entity); + + Assert.True(translatedResult); + Assert.Equal(mappedResult, translatedResult); + } + + [Fact] + public void BinaryExpressionOfEnumsToString() + { + Expression> mappedExpression; + { + var param = Expression.Parameter(typeof(EntityDto), "x"); + var property = Expression.Property(param, nameof(EntityDto.Value)); + var constant = Expression.Constant(SimpleEnum.Value2, typeof(SimpleEnum)); + var binaryExpression = Expression.Equal(property, constant); + var lambdaExpression = Expression.Lambda(binaryExpression, param); + mappedExpression = Mapper.Map>>(lambdaExpression); + } + + Expression> translatedExpression = x => x.Value == "Value2"; + + var mappedExpressionDelegate = mappedExpression.Compile(); + var translatedExpressionDelegate = translatedExpression.Compile(); + + var entity = new EntityAsString { Value = SimpleEnum.Value2.ToString() }; + var mappedResult = mappedExpressionDelegate(entity); + var translatedResult = translatedExpressionDelegate(entity); + + Assert.True(translatedResult); + Assert.Equal(mappedResult, translatedResult); + } + + [Fact] + public void BinaryExpressionOfCoalescedEnumToInt() + { + Expression> mappedExpression; + { + var param = Expression.Parameter(typeof(EntityDto), "x"); + var property = Expression.Property(param, nameof(EntityDto.Value)); + var usedConstant = Expression.Constant(SimpleEnum.Value2, typeof(SimpleEnum?)); + var otherConstant = Expression.Constant(SimpleEnum.Value1, typeof(SimpleEnum)); + var coalesce = Expression.Coalesce(usedConstant, otherConstant); + var binaryExpression = Expression.Equal(property, coalesce); + var lambdaExpression = Expression.Lambda(binaryExpression, param); + mappedExpression = Mapper.Map>>(lambdaExpression); + } + + Expression> translatedExpression = translatedExpression = x => x.Value == ((int?)2 ?? 1); + + var mappedExpressionDelegate = mappedExpression.Compile(); + var translatedExpressionDelegate = translatedExpression.Compile(); + + var entity = new EntityAsInt { Value = (int)SimpleEnum.Value2 }; + var mappedResult = mappedExpressionDelegate(entity); + var translatedResult = translatedExpressionDelegate(entity); + + Assert.True(translatedResult); + Assert.Equal(mappedResult, translatedResult); + } + } +} From 8ce9f43c026b656573d921edac125a1eeea23daf Mon Sep 17 00:00:00 2001 From: ErikGjers Date: Thu, 16 Feb 2023 19:48:48 +0100 Subject: [PATCH 2/5] Enum to int conversion logic from VisitBinary to VisitConstant. --- .../Extensions/VisitorExtensions.cs | 9 +++++++++ .../XpressionMapperVisitor.cs | 17 ++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/AutoMapper.Extensions.ExpressionMapping/Extensions/VisitorExtensions.cs b/src/AutoMapper.Extensions.ExpressionMapping/Extensions/VisitorExtensions.cs index decffed..f876a97 100644 --- a/src/AutoMapper.Extensions.ExpressionMapping/Extensions/VisitorExtensions.cs +++ b/src/AutoMapper.Extensions.ExpressionMapping/Extensions/VisitorExtensions.cs @@ -5,6 +5,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using AutoMapper.Extensions.ExpressionMapping.Structures; +using AutoMapper.Internal; namespace AutoMapper.Extensions.ExpressionMapping.Extensions { @@ -202,5 +203,13 @@ public static List GetUnderlyingGenericTypes(this Type type) => type == null || !type.GetTypeInfo().IsGenericType ? new List() : type.GetGenericArguments().ToList(); + + public static bool IsEnumType(this Type type) + { + if (type.IsNullableType()) + type = Nullable.GetUnderlyingType(type); + + return type.IsEnum(); + } } } diff --git a/src/AutoMapper.Extensions.ExpressionMapping/XpressionMapperVisitor.cs b/src/AutoMapper.Extensions.ExpressionMapping/XpressionMapperVisitor.cs index c9970f0..b186c10 100644 --- a/src/AutoMapper.Extensions.ExpressionMapping/XpressionMapperVisitor.cs +++ b/src/AutoMapper.Extensions.ExpressionMapping/XpressionMapperVisitor.cs @@ -431,17 +431,6 @@ Expression DoVisitBinary(Expression newLeft, Expression newRight, Expression con { if (newLeft != node.Left || newRight != node.Right || conversion != node.Conversion) { - if(node.Left.Type.IsEnum && node.Right.Type.IsEnum) - { - if (newLeft.Type.IsEnum && !newRight.Type.IsEnum) - { - newLeft = Expression.Convert(newLeft, newRight.Type); - } - else if (!newLeft.Type.IsEnum && newRight.Type.IsEnum) - { - newRight = Expression.Convert(newRight, newLeft.Type); - } - } if (node.NodeType == ExpressionType.Coalesce && node.Conversion != null) return Expression.Coalesce(newLeft, newRight, conversion as LambdaExpression); else @@ -530,10 +519,16 @@ protected override Expression VisitConstant(ConstantExpression node) if (ConfigurationProvider.CanMapConstant(node.Type, newType)) return base.VisitConstant(Expression.Constant(Mapper.MapObject(node.Value, node.Type, newType), newType)); + + else if (BothEnumOrLiteral()) + return node.ConvertTypeIfNecessary(newType); //Issue 3455 (Non-Generic Mapper.Map failing for structs in v10) //return base.VisitConstant(Expression.Constant(Mapper.Map(node.Value, node.Type, newType), newType)); } return base.VisitConstant(node); + + bool BothEnumOrLiteral() + => (node.Type.IsLiteralType() || node.Type.IsEnumType()) && (newType.IsLiteralType() || newType.IsEnumType()); } protected override Expression VisitMethodCall(MethodCallExpression node) From 0cde2e7686bf1ee7d75a085e317554481a885f8b Mon Sep 17 00:00:00 2001 From: Erik Gjers Date: Thu, 2 Mar 2023 08:18:06 +0100 Subject: [PATCH 3/5] new test start --- .../ExpressionMappingEnumToIntOrString.cs | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToIntOrString.cs b/tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToIntOrString.cs index 924e376..0a90a50 100644 --- a/tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToIntOrString.cs +++ b/tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToIntOrString.cs @@ -32,6 +32,31 @@ public string Convert(SimpleEnum source, string destination, ResolutionContext c return source.ToString(); } } + + private class ComplexEntity + { + //Named in the direction of conversion for entity -> dto + public int intToEnum { get; set; } + public string stringToEnum { get; set; } + public SimpleEnum enumToInt { get; set; } + public SimpleEnum enumToString { get; set; } + //Untranslated properties + public SimpleEnum enumToEnum { get; set; } + public int intToInt { get; set; } + public string stringToString { get; set; } + } + + private class ComplexEntityDto + { + public SimpleEnum intToEnum { get; set; } + public SimpleEnum stringToEnum { get; set; } + public int enumToInt { get; set; } + public string enumToString { get; set; } + + public SimpleEnum enumToEnum { get; set; } + public int intToInt { get; set; } + public string stringToString { get; set; } + } protected override MapperConfiguration Configuration { get @@ -44,6 +69,18 @@ protected override MapperConfiguration Configuration config.CreateMap() .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)); config.CreateMap().ConvertUsing(); + + config.CreateMap() + .ForMember(dest => dest.intToEnum, config => config.MapFrom(src => src.intToEnum)) + .ForMember(dest => dest.stringToEnum, config => config.MapFrom(src => src.stringToEnum)) + .ForMember(dest => dest.enumToEnum, config => config.MapFrom(src => src.enumToEnum)) + .ForMember(dest => dest.enumToInt, config => config.MapFrom(src => src.enumToInt)) + .ForMember(dest => dest.enumToString, config => config.MapFrom(src => src.enumToString.ToString())) + .ForMember(dest => dest.intToInt, config => config.MapFrom(src => src.intToInt)) + .ForMember(dest => dest.stringToString, config => config.MapFrom(src => src.stringToString)) + .ReverseMap(); + + //config.CreateMap().ConvertUsing(e => e.ToString()); }); } } @@ -127,5 +164,78 @@ public void BinaryExpressionOfCoalescedEnumToInt() Assert.True(translatedResult); Assert.Equal(mappedResult, translatedResult); } + + //TODO: Make the a Theory. + [Fact] + public void BinaryExpressionPartialTranslation() + { + + //x => x.IntToEnum == 1 && x.OtherInt == 2; + Expression> mappedExpression; + { + var param = Expression.Parameter(typeof(ComplexEntity), "x"); + var property1 = Expression.Property(param, nameof(ComplexEntity.intToEnum)); + var property2 = Expression.Property(param, nameof(ComplexEntity.intToInt)); + var property3 = Expression.Property(param, nameof(ComplexEntity.stringToString)); + var property4 = Expression.Property(param, nameof(ComplexEntity.stringToEnum)); + var property5 = Expression.Property(param, nameof(ComplexEntity.enumToEnum)); + var property6 = Expression.Property(param, nameof(ComplexEntity.enumToInt)); + var property7 = Expression.Property(param, nameof(ComplexEntity.enumToString)); + + var constant1 = Expression.Constant(2, typeof(int)); + var constant2 = Expression.Constant(1, typeof(int)); + var constant3 = Expression.Constant(SimpleEnum.Value2.ToString(), typeof(string)); + var constant4 = Expression.Constant(SimpleEnum.Value1.ToString(), typeof(string)); + var constant5 = Expression.Constant(SimpleEnum.Value3, typeof(SimpleEnum)); + var constant6 = Expression.Constant(SimpleEnum.Value2, typeof(SimpleEnum)); + var constant7 = Expression.Constant(SimpleEnum.Value1, typeof(SimpleEnum)); + + Expression[] equals = new Expression[]{ + Expression.Equal(property1, constant1), + Expression.Equal(property2, constant2), + Expression.Equal(property3, constant3), + Expression.Equal(property4, constant4), + Expression.Equal(property5, constant5), + Expression.Equal(property6, constant6), + Expression.Equal(property7, constant7) + }; + + //var equals1 = Expression.Equal(property1, constant1); + //var equals2 = Expression.Equal(property2, constant2); + //var equals3 = Expression.Equal(property3, constant3); + //var equals4 = Expression.Equal(property4, constant4); + //var equals5 = Expression.Equal(property5, constant5); + //var equals6 = Expression.Equal(property6, constant6); + //var equals7 = Expression.Equal(property7, constant7); + Expression andExpression = equals[0]; + for (int i = 1; i < equals.Length; i++) + { + andExpression = Expression.And(andExpression, equals[i]); + } + var lambdaExpression = Expression.Lambda(andExpression, param); + mappedExpression = Mapper.Map>>(lambdaExpression); + } + + Expression> translatedExpression = + translatedExpression = x => + x.intToEnum == SimpleEnum.Value2 + && x.intToInt == (int)SimpleEnum.Value1 + && x.stringToString == SimpleEnum.Value2.ToString() + && x.stringToEnum == SimpleEnum.Value1 + && x.enumToEnum == SimpleEnum.Value3 + && x.enumToInt == (int)SimpleEnum.Value2 + && x.enumToString == SimpleEnum.Value1.ToString() + ; + + var mappedExpressionDelegate = mappedExpression.Compile(); + var translatedExpressionDelegate = translatedExpression.Compile(); + + var entity = new ComplexEntityDto { intToEnum = SimpleEnum.Value2, intToInt = 1 }; + var mappedResult = mappedExpressionDelegate(entity); + var translatedResult = translatedExpressionDelegate(entity); + + Assert.True(translatedResult); + Assert.Equal(mappedResult, translatedResult); + } } } From 3104e1dc038e53e6824e0a8584c35de76ebb7f41 Mon Sep 17 00:00:00 2001 From: Erik Gjers Date: Thu, 2 Mar 2023 09:25:23 +0100 Subject: [PATCH 4/5] Made tests using Theory --- .../ExpressionMappingEnumToIntOrString.cs | 131 -------- .../ExpressionMappingEnumToNumericOrString.cs | 288 ++++++++++++++++++ 2 files changed, 288 insertions(+), 131 deletions(-) delete mode 100644 tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToIntOrString.cs create mode 100644 tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToNumericOrString.cs diff --git a/tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToIntOrString.cs b/tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToIntOrString.cs deleted file mode 100644 index 924e376..0000000 --- a/tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToIntOrString.cs +++ /dev/null @@ -1,131 +0,0 @@ -using Newtonsoft.Json.Linq; -using System; -using System.Linq.Expressions; -using Xunit; - -namespace AutoMapper.Extensions.ExpressionMapping.UnitTests -{ - public class ExpressionMappingEnumToIntOrString : AutoMapperSpecBase - { - public enum SimpleEnum - { - Value1 = 1, - Value2 = 2, - Value3 = 3 - } - private class EntityDto - { - internal SimpleEnum Value { get; set; } - } - private class EntityAsString - { - internal string Value { get; set; } - } - private class EntityAsInt - { - internal int Value { get; set; } - } - public class SimpleEnumToStringTypeResolver : ITypeConverter - { - public string Convert(SimpleEnum source, string destination, ResolutionContext context) - { - return source.ToString(); - } - } - protected override MapperConfiguration Configuration - { - get - { - return new MapperConfiguration(config => - { - config.AddExpressionMapping(); - config.CreateMap() - .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)); - config.CreateMap() - .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)); - config.CreateMap().ConvertUsing(); - }); - } - } - - [Fact] - public void BinaryExpressionOfEnumsToInt() - { - Expression> mappedExpression; - { - var param = Expression.Parameter(typeof(EntityDto), "x"); - var property = Expression.Property(param, nameof(EntityDto.Value)); - var constant = Expression.Constant(SimpleEnum.Value2, typeof(SimpleEnum)); - var binaryExpression = Expression.Equal(property, constant); - var lambdaExpression = Expression.Lambda(binaryExpression, param); - mappedExpression = Mapper.Map>>(lambdaExpression); - } - - Expression> translatedExpression= translatedExpression = x => x.Value == 2; - - var mappedExpressionDelegate = mappedExpression.Compile(); - var translatedExpressionDelegate = translatedExpression.Compile(); - - var entity = new EntityAsInt { Value = (int)SimpleEnum.Value2 }; - var mappedResult = mappedExpressionDelegate(entity); - var translatedResult = translatedExpressionDelegate(entity); - - Assert.True(translatedResult); - Assert.Equal(mappedResult, translatedResult); - } - - [Fact] - public void BinaryExpressionOfEnumsToString() - { - Expression> mappedExpression; - { - var param = Expression.Parameter(typeof(EntityDto), "x"); - var property = Expression.Property(param, nameof(EntityDto.Value)); - var constant = Expression.Constant(SimpleEnum.Value2, typeof(SimpleEnum)); - var binaryExpression = Expression.Equal(property, constant); - var lambdaExpression = Expression.Lambda(binaryExpression, param); - mappedExpression = Mapper.Map>>(lambdaExpression); - } - - Expression> translatedExpression = x => x.Value == "Value2"; - - var mappedExpressionDelegate = mappedExpression.Compile(); - var translatedExpressionDelegate = translatedExpression.Compile(); - - var entity = new EntityAsString { Value = SimpleEnum.Value2.ToString() }; - var mappedResult = mappedExpressionDelegate(entity); - var translatedResult = translatedExpressionDelegate(entity); - - Assert.True(translatedResult); - Assert.Equal(mappedResult, translatedResult); - } - - [Fact] - public void BinaryExpressionOfCoalescedEnumToInt() - { - Expression> mappedExpression; - { - var param = Expression.Parameter(typeof(EntityDto), "x"); - var property = Expression.Property(param, nameof(EntityDto.Value)); - var usedConstant = Expression.Constant(SimpleEnum.Value2, typeof(SimpleEnum?)); - var otherConstant = Expression.Constant(SimpleEnum.Value1, typeof(SimpleEnum)); - var coalesce = Expression.Coalesce(usedConstant, otherConstant); - var binaryExpression = Expression.Equal(property, coalesce); - var lambdaExpression = Expression.Lambda(binaryExpression, param); - mappedExpression = Mapper.Map>>(lambdaExpression); - } - - Expression> translatedExpression = translatedExpression = x => x.Value == ((int?)2 ?? 1); - - var mappedExpressionDelegate = mappedExpression.Compile(); - var translatedExpressionDelegate = translatedExpression.Compile(); - - var entity = new EntityAsInt { Value = (int)SimpleEnum.Value2 }; - var mappedResult = mappedExpressionDelegate(entity); - var translatedResult = translatedExpressionDelegate(entity); - - Assert.True(translatedResult); - Assert.Equal(mappedResult, translatedResult); - } - } -} diff --git a/tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToNumericOrString.cs b/tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToNumericOrString.cs new file mode 100644 index 0000000..4439f61 --- /dev/null +++ b/tests/AutoMapper.Extensions.ExpressionMapping.UnitTests/ExpressionMappingEnumToNumericOrString.cs @@ -0,0 +1,288 @@ + +using System; +using System.Linq.Expressions; +using Xunit; + +namespace AutoMapper.Extensions.ExpressionMapping.UnitTests +{ + public class ExpressionMappingEnumToNumericOrString : AutoMapperSpecBase + { + public enum SimpleEnumByte : byte + { + Value1 = 1, + Value2 = 2, + Value3 = 3 + } + public enum SimpleEnumSByte : sbyte + { + Value1 = 1, + Value2 = 2, + Value3 = 3 + } + public enum SimpleEnumShort : short + { + Value1 = 1, + Value2 = 2, + Value3 = 3 + } + public enum SimpleEnumUShort : ushort + { + Value1 = 1, + Value2 = 2, + Value3 = 3 + } + public enum SimpleEnumInt : int + { + Value1 = 1, + Value2 = 2, + Value3 = 3 + } + public enum SimpleEnumUInt : uint + { + Value1 = 1, + Value2 = 2, + Value3 = 3 + } + public enum SimpleEnumLong : long + { + Value1 = 1, + Value2 = 2, + Value3 = long.MaxValue + } + public enum SimpleEnumULong : ulong + { + Value1 = 1, + Value2 = 2, + Value3 = long.MaxValue + } + private class EntityDto + where TEnum : Enum + { + internal TEnum Value { get; set; } + } + private class Entity + { + internal T Value { get; set; } + } + + protected override MapperConfiguration Configuration + { + get + { + return new MapperConfiguration(config => + { + config.AddExpressionMapping(); + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + config.CreateMap, EntityDto>() + .ForMember(dest => dest.Value, config => config.MapFrom(src => src.Value)) + .ReverseMap(); + + config.CreateMap().ConvertUsing(e => e.ToString()); + config.CreateMap().ConvertUsing(e => e.ToString()); + config.CreateMap().ConvertUsing(e => e.ToString()); + config.CreateMap().ConvertUsing(e => e.ToString()); + config.CreateMap().ConvertUsing(e => e.ToString()); + config.CreateMap().ConvertUsing(e => e.ToString()); + config.CreateMap().ConvertUsing(e => e.ToString()); + config.CreateMap().ConvertUsing(e => e.ToString()); + }); + } + } + + [Fact] + public void BinaryExpressionOfEnumsToInt() + { + Expression, bool>> mappedExpression; + { + var param = Expression.Parameter(typeof(EntityDto), "x"); + var property = Expression.Property(param, nameof(EntityDto.Value)); + var constant = Expression.Constant(SimpleEnumInt.Value2, typeof(SimpleEnumInt)); + var binaryExpression = Expression.Equal(property, constant); + var lambdaExpression = Expression.Lambda(binaryExpression, param); + mappedExpression = Mapper.Map, bool>>>(lambdaExpression); + } + + Expression, bool>> translatedExpression = translatedExpression = x => x.Value == 2; + + var mappedExpressionDelegate = mappedExpression.Compile(); + var translatedExpressionDelegate = translatedExpression.Compile(); + + var entity = new Entity { Value = (int)SimpleEnumInt.Value2 }; + var mappedResult = mappedExpressionDelegate(entity); + var translatedResult = translatedExpressionDelegate(entity); + + Assert.True(translatedResult); + Assert.Equal(mappedResult, translatedResult); + } + + [Theory] + [InlineData(SimpleEnumByte.Value2, (byte)2)] + [InlineData(SimpleEnumSByte.Value2, (sbyte)2)] + [InlineData(SimpleEnumShort.Value2, (short)2)] + [InlineData(SimpleEnumUShort.Value2, (ushort)2)] + [InlineData(SimpleEnumInt.Value2, 2)] + [InlineData(SimpleEnumUInt.Value2, 2U)] + [InlineData(SimpleEnumLong.Value2, 2L)] + [InlineData(SimpleEnumULong.Value2, 2UL)] + [InlineData(SimpleEnumSByte.Value2, (sbyte)1)] + [InlineData(SimpleEnumByte.Value2, (byte)1)] + [InlineData(SimpleEnumShort.Value2, (short)1)] + [InlineData(SimpleEnumUShort.Value2, (ushort)1)] + [InlineData(SimpleEnumInt.Value2, 1)] + [InlineData(SimpleEnumUInt.Value2, 1U)] + [InlineData(SimpleEnumLong.Value2, 1L)] + [InlineData(SimpleEnumULong.Value2, 1UL)] + [InlineData(SimpleEnumSByte.Value3, (sbyte)3)] + [InlineData(SimpleEnumByte.Value3, (byte)1)] + [InlineData(SimpleEnumShort.Value3, (short)1)] + [InlineData(SimpleEnumUShort.Value3, (ushort)1)] + [InlineData(SimpleEnumInt.Value3, 1)] + [InlineData(SimpleEnumUInt.Value3, 1U)] + [InlineData(SimpleEnumLong.Value3, 1L)] + [InlineData(SimpleEnumULong.Value3, 1UL)] + [InlineData(SimpleEnumSByte.Value3, (sbyte)3)] + [InlineData(SimpleEnumByte.Value3, (byte)3)] + [InlineData(SimpleEnumShort.Value3, (short)3)] + [InlineData(SimpleEnumUShort.Value3, (ushort)3)] + [InlineData(SimpleEnumInt.Value3, 3)] + [InlineData(SimpleEnumUInt.Value3, 3U)] + [InlineData(SimpleEnumLong.Value3, 3L)] + [InlineData(SimpleEnumULong.Value3, 3UL)] + [InlineData(SimpleEnumLong.Value3, long.MaxValue)] + [InlineData(SimpleEnumULong.Value3, (ulong)long.MaxValue)] + public void BinaryExpressionEquals(TEnum enumConstant, TNumeric numericConstant) + where TEnum : Enum + { + var correctResult = ((TNumeric)(object)enumConstant).Equals(numericConstant); + Expression, bool>> mappedExpression; + { + var param = Expression.Parameter(typeof(EntityDto), "x"); + var property = Expression.Property(param, nameof(EntityDto.Value)); + var constantExp = Expression.Constant(enumConstant, typeof(TEnum)); + var binaryExpression = Expression.Equal(property, constantExp); + var lambdaExpression = Expression.Lambda(binaryExpression, param); + mappedExpression = Mapper.Map, bool>>>(lambdaExpression); + } + + var mappedExpressionDelegate = mappedExpression.Compile(); + + var entity = new Entity { Value = numericConstant }; + var result = mappedExpressionDelegate(entity); + + Assert.Equal(result, correctResult); + } + + + + [Theory] + [InlineData(SimpleEnumSByte.Value2, (sbyte)1)] + [InlineData(SimpleEnumByte.Value2, (byte)1)] + [InlineData(SimpleEnumShort.Value2, (short)1)] + [InlineData(SimpleEnumUShort.Value2, (ushort)1)] + [InlineData(SimpleEnumInt.Value2, 1)] + [InlineData(SimpleEnumUInt.Value2, 1U)] + [InlineData(SimpleEnumLong.Value2, 1L)] + [InlineData(SimpleEnumULong.Value2, 1UL)] + public void BinaryExpressionEqualsFalse(TEnum twoAsEnum, TNumeric oneAsNumeric) + where TEnum : Enum + { + Expression, bool>> mappedExpression; + { + var param = Expression.Parameter(typeof(EntityDto), "x"); + var property = Expression.Property(param, nameof(EntityDto.Value)); + var constantExp = Expression.Constant(twoAsEnum, typeof(TEnum)); + var binaryExpression = Expression.Equal(property, constantExp); + var lambdaExpression = Expression.Lambda(binaryExpression, param); + mappedExpression = Mapper.Map, bool>>>(lambdaExpression); + } + var mappedExpressionDelegate = mappedExpression.Compile(); + var entity = new Entity { Value = oneAsNumeric }; + var result = mappedExpressionDelegate(entity); + + Assert.False(result); + } + + [Theory] + [InlineData(SimpleEnumSByte.Value2)] + [InlineData(SimpleEnumByte.Value2)] + [InlineData(SimpleEnumShort.Value2)] + [InlineData(SimpleEnumUShort.Value2)] + [InlineData(SimpleEnumInt.Value2)] + [InlineData(SimpleEnumUInt.Value2)] + [InlineData(SimpleEnumLong.Value2)] + [InlineData(SimpleEnumULong.Value2)] + public void BinaryExpressionEqualsWithString(TEnum twoAsEnum) + where TEnum : Enum + { + + var constant = typeof(TEnum).GetEnumValues().GetValue(1); + Expression, bool>> mappedExpression; + { + var param = Expression.Parameter(typeof(EntityDto), "x"); + var property = Expression.Property(param, nameof(EntityDto.Value)); + var constantExp = Expression.Constant(constant); + var binaryExpression = Expression.Equal(property, constantExp); + var lambdaExpression = Expression.Lambda(binaryExpression, param); + mappedExpression = Mapper.Map, bool>>>(lambdaExpression); + } + + var mappedExpressionDelegate = mappedExpression.Compile(); + + var entity = new Entity { Value = constant.ToString()}; + var result = mappedExpressionDelegate(entity); + + Assert.True(result); + + var notEqualConstant = typeof(TEnum).GetEnumValues().GetValue(2); + entity = new Entity { Value = notEqualConstant.ToString() }; + result = mappedExpressionDelegate(entity); + + Assert.False(result); + } + } +} From 1946f01a422efe6e7813e164d32bc117c709913b Mon Sep 17 00:00:00 2001 From: Blaise Taylor Date: Mon, 13 Mar 2023 15:48:56 -0400 Subject: [PATCH 5/5] Logic already in VisitMember solves the same problem - should be safer to reuse. --- .../XpressionMapperVisitor.cs | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/AutoMapper.Extensions.ExpressionMapping/XpressionMapperVisitor.cs b/src/AutoMapper.Extensions.ExpressionMapping/XpressionMapperVisitor.cs index b186c10..1f9fa78 100644 --- a/src/AutoMapper.Extensions.ExpressionMapping/XpressionMapperVisitor.cs +++ b/src/AutoMapper.Extensions.ExpressionMapping/XpressionMapperVisitor.cs @@ -95,7 +95,7 @@ Expression GetMappedMemberExpression(Expression parentExpression, List propertyMapInfoList, PropertyMapInfo lastWithCustExpression, Expression mappedParentExpr) => GetMemberExpressionFromCustomExpression ( @@ -519,16 +536,10 @@ protected override Expression VisitConstant(ConstantExpression node) if (ConfigurationProvider.CanMapConstant(node.Type, newType)) return base.VisitConstant(Expression.Constant(Mapper.MapObject(node.Value, node.Type, newType), newType)); - - else if (BothEnumOrLiteral()) - return node.ConvertTypeIfNecessary(newType); //Issue 3455 (Non-Generic Mapper.Map failing for structs in v10) //return base.VisitConstant(Expression.Constant(Mapper.Map(node.Value, node.Type, newType), newType)); } return base.VisitConstant(node); - - bool BothEnumOrLiteral() - => (node.Type.IsLiteralType() || node.Type.IsEnumType()) && (newType.IsLiteralType() || newType.IsEnumType()); } protected override Expression VisitMethodCall(MethodCallExpression node)