Skip to content

Can't map to ImmutableArray #3850

@drew-cooper

Description

@drew-cooper

Source/destination types

public class Foo
{
	public string Value { get; set; }
}

public class Bar
{
	public ImmutableArray<byte> Value { get; set; }
}

Mapping configuration

new MapperConfiguration(cfg => 
    cfg.CreateMap<Foo, Bar>()
        .ForMember(
            bar => bar.Value, 
            m => m.MapFrom((foo, bar) => Convert.FromBase64String(foo.Value).ToImmutableArray())));

Version: 10.1.1 and 11.0.0

Expected behavior

bar.Value should be assigned the ImmutableArray<byte> created by the MapFrom delegate.

Actual behavior

On v10.1.1 an exception was thrown while building the configuration:

The binary operator Equal is not defined for the types 'System.Collections.Immutable.ImmutableArray`1[System.Byte]' and 'System.Object'.
   at System.Linq.Expressions.Expression.GetEqualityComparisonOperator(ExpressionType binaryType, String opName, Expression left, Expression right, Boolean liftToNull)
   at System.Linq.Expressions.Expression.Equal(Expression left, Expression right, Boolean liftToNull, MethodInfo method)
   at System.Linq.Expressions.Expression.Equal(Expression left, Expression right)
   at AutoMapper.Internal.CollectionMapperExpressionFactory.<MapCollectionExpression>g__UseDestinationValue|1_0(<>c__DisplayClass1_0& )
   at AutoMapper.Internal.CollectionMapperExpressionFactory.MapCollectionExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, IMemberMap memberMap, Expression sourceExpression, Expression destExpression, Expression contextExpression, Type ifInterfaceType, MapItem mapItem)
   at AutoMapper.Mappers.CollectionMapper.MapExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, IMemberMap memberMap, Expression sourceExpression, Expression destExpression, Expression contextExpression)
   at AutoMapper.Execution.ExpressionBuilder.ObjectMapperExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, TypePair typePair, Expression sourceParameter, Expression contextParameter, IMemberMap propertyMap, Expression destinationParameter)
   at AutoMapper.Execution.ExpressionBuilder.MapExpression(IConfigurationProvider configurationProvider, ProfileMap profileMap, TypePair typePair, Expression sourceParameter, Expression contextParameter, IMemberMap propertyMap, Expression destinationParameter)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreatePropertyMapFunc(IMemberMap memberMap, Expression destination, MemberInfo destinationMember)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreateAssignmentFunc(Expression destinationFunc)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreateMapperLambda(HashSet`1 typeMapsPath)
   at AutoMapper.TypeMap.CreateMapperLambda(IConfigurationProvider configurationProvider, HashSet`1 typeMapsPath)
   at AutoMapper.TypeMap.Seal(IConfigurationProvider configurationProvider)
   at AutoMapper.MapperConfiguration.Seal()
   at AutoMapper.MapperConfiguration..ctor(MapperConfigurationExpression configurationExpression)
   at AutoMapper.MapperConfiguration..ctor(Action`1 configure)
   at UserQuery.Main(), line 3

I tried v11.0.0 and it failed similarly, but with a different exception:

InvalidOperationException: Coalesce used with type that cannot be null
   at System.Linq.Expressions.Expression.ValidateCoalesceArgTypes(Type left, Type right)
   at System.Linq.Expressions.Expression.Coalesce(Expression left, Expression right, LambdaExpression conversion)
   at System.Linq.Expressions.Expression.Coalesce(Expression left, Expression right)
   at AutoMapper.Internal.Mappers.CollectionMapper.<MapExpression>g__UseDestinationValue|2_3(<>c__DisplayClass2_1& )
   at AutoMapper.Internal.Mappers.CollectionMapper.<MapExpression>g__MapCollectionCore|2_1(Expression destExpression, <>c__DisplayClass2_0& )
   at AutoMapper.Internal.Mappers.CollectionMapper.MapExpression(IGlobalConfiguration configurationProvider, ProfileMap profileMap, MemberMap memberMap, Expression sourceExpression, Expression destExpression)
   at AutoMapper.Execution.ExpressionBuilder.MapExpression(IGlobalConfiguration configurationProvider, ProfileMap profileMap, TypePair typePair, Expression sourceParameter, MemberMap propertyMap, Expression destinationParameter)
   at AutoMapper.Execution.TypeMapPlanBuilder.MapMember(MemberMap memberMap, Expression destinationMemberValue, ParameterExpression resolvedValue)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreatePropertyMapFunc(MemberMap memberMap, Expression destination, MemberInfo destinationMember)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreateAssignmentFunc(Expression createDestination)
   at AutoMapper.Execution.TypeMapPlanBuilder.CreateMapperLambda(HashSet`1 typeMapsPath)
   at AutoMapper.TypeMap.CreateMapperLambda(IGlobalConfiguration configurationProvider, HashSet`1 typeMapsPath)
   at AutoMapper.TypeMap.Seal(IGlobalConfiguration configurationProvider, HashSet`1 typeMapsPath)
   at AutoMapper.MapperConfiguration.Seal()
   at AutoMapper.MapperConfiguration..ctor(MapperConfigurationExpression configurationExpression)
   at AutoMapper.MapperConfiguration..ctor(Action`1 configure)
   at UserQuery.Main(), line 3

Steps to reproduce

Running the above code is all that is needed. Here is a minimal LinqPad query that repros the issue:

<Query Kind="Program">
  <NuGetReference Version="10.1.1">AutoMapper</NuGetReference>
  <Namespace>AutoMapper</Namespace>
  <Namespace>System.Collections.Immutable</Namespace>
  <Namespace>System.Threading.Tasks</Namespace>
</Query>

void Main()
{
	new MapperConfiguration(cfg => 
		cfg.CreateMap<Foo, Bar>()
			.ForMember(
				bar => bar.Value, 
				m => m.MapFrom((foo, bar) => Convert.FromBase64String(foo.Value).ToImmutableArray())));
}

// You can define other methods, fields, classes and namespaces here
public class Foo
{
	public string Value { get; set; }
}

public class Bar
{
	public ImmutableArray<byte> Value { get; set; }
}

Notes

  • In my use-case I know that the source string value will not be null.
  • I can work around this by ignoring the member in configuration and doing the mapping in the AfterMap action.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions