Skip to content

Mapping of conditional expressions throws a null reference exception #45

@Erythnul

Description

@Erythnul

Hi,

We are using your expression mapping to translate our mapping expressions from our DTO definitions into our entity definitions, however, we're running into a problem. This is

We are mapping a conditional expression on a nested class, as shown in the following code:

    public class TestEntity
    {
        public Guid Id { get; set; }
        public int ConditionField { get; set; }
        public int ToBeNestedField { get; set; }
    }

    public class TestDTO
    {
        public Guid Id { get; set; }
        public TestDTONestedClass NestedClass { get; set; }
    }
    public class TestDTONestedClass
    {
        public int? NestedField { get; set; }
    }

    public class TestMapperProfile : Profile
    {
        public TestMapperProfile()
        {
            CreateMap<TestEntity, TestDTO>()
                .ForMember(dst => dst.Id, opt => opt.MapFrom(src => src.Id))
                .ForMember(dst => dst.NestedClass, opt => opt.MapFrom(src => src.ConditionField == 1 ? src : default));

            CreateMap<TestEntity, TestDTONestedClass>()
                .ForMember(dst => dst.NestedField, opt => opt.MapFrom(src => (int?)src.ToBeNestedField));
        }
    }

    [TestMethod]
    public void MapExpressionOnNestedClass()
    {
        var config = new MapperConfiguration(cfg =>
        {
            cfg.AddProfile(new TestMapperProfile());
            cfg.AddExpressionMapping();
        });

        var mapper = new Mapper(config);

        Expression<Func<TestDTO, bool>> expr = x => x.NestedClass.NestedField == 1;

        var mappedExpression = mapper.Map<Expression<Func<TestEntity, bool>>>(expr); //throws

        Assert.IsNotNull(mappedExpression);
    }

When the code arrives at the GetMemberFullName() method, it tries to cast the Conditional Expression to a MemberExpression, which results in a null reference exception, as shown below:

	public static string GetMemberFullName(this LambdaExpression expr)
	{
		if (expr.Body.NodeType == ExpressionType.Parameter)
			return string.Empty;
	
		MemberExpression me;
		switch (expr.Body.NodeType) // NodeType == ExpressionType.Conditional
		{
			case ExpressionType.Convert:
			case ExpressionType.ConvertChecked:
				me = (expr.Body as UnaryExpression)?.Operand as MemberExpression;
				break;
			default:
				me = expr.Body as MemberExpression; //Results in me == null
				break;
		}
	
		return me.GetPropertyFullName();
	}
	
	public static string GetPropertyFullName(this Expression expression)
	{
		const string period = ".";
	
		//the node represents parameter of the expression
		switch (expression.NodeType) // throws NullReferenceException
		{
			case ExpressionType.MemberAccess:
				var memberExpression = (MemberExpression)expression;
				var parentFullName = memberExpression.Expression.GetPropertyFullName();
				return string.IsNullOrEmpty(parentFullName)
					? memberExpression.Member.Name
					: string.Concat(memberExpression.Expression.GetPropertyFullName(), period, memberExpression.Member.Name);
			default:
				return string.Empty;
		}
	}

Returning an empty string (as would be the default in GetPropertyFullName) seems to work well, but I can't tell if this would break anything else because of my lack of knowledge about the rest of the code in this package.

In our case, I think we're expecting the input expression to be mapped as follows:

Input: x => (x.NestedClass.NestedField == Convert(1))

Output: x => (Convert(x.ToBeNestedField) == Convert(1))

Would love to hear any advice! Thanks in advance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions