-
Notifications
You must be signed in to change notification settings - Fork 54
Description
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.