如何将属性映射到可能为null的子属性?
例如,以下代码将因NullReferenceException而失败,因为Contact的User属性为null。
using AutoMapper;
namespace AutoMapperTests
{
class Program
{
static void Main( string[] args )
{
Mapper.CreateMap<Contact, ContactModel>()
.ForMember( x => x.UserName, opt => opt.MapFrom( y => y.User.UserName ) );
Mapper.AssertConfigurationIsValid();
var c = new Contact();
var co = new ContactModel();
Mapper.Map( c, co );
}
}
public class User
{
public string UserName { get; set; }
}
public class Contact
{
public User User { get; set; }
}
public class ContactModel
{
public string UserName { get; set; }
}
}
我想将ContactModel的UserName默认为空字符串。
我已经尝试过NullSubstitute方法,但我认为它试图使用User.Username,而不是仅仅在User属性上。
您可以编写如下映射代码:
Mapper.CreateMap<Contact, ContactModel>()
.ForMember( x => x.UserName, opt => opt.MapFrom( y => (y.User != null) ? y.User.UserName : "" ) );
这将检查User
是否为null,然后分配一个emtpy字符串或UserName
。
如果你发现自己在Dave的答案中做了大量的空检查,你可能会考虑应用我刚才发表的博客:Getting rid of null checks in property chains。这将允许你写这个:
Mapper.CreateMap<Contact, ContactModel>()
.ForMember(x => x.UserName,
opt => opt.NullSafeMapFrom(y => y.User.UserName) ?? string.Empty);
我使用的解决方案是在原始委托周围创建一个闭包,它将它包装在try / catch块中。遗憾的是,必须使用Expression.Compile()
来阻止Visual Studio在原始委托中抛出异常时捕获异常。可能不建议在高性能环境中使用,但我在常规UI中使用它时从未遇到过任何问题。你的milage可能会有所不同。
public static class AutoMapperExtensions
{
public static void NullSafeMapFrom<T, TResult>(this IMemberConfigurationExpression<T> opt, Expression<Func<T, TResult>> sourceMemberExpression)
{
var sourceMember = sourceMemberExpression.Compile();
opt.MapFrom(src =>
{
try
{
return sourceMember(src);
}
catch (NullReferenceException)
{}
return default(TResult);
});
}
}
.ForMember(dest => dest.Target, opt => opt.NullSafeMapFrom(src => src.Something.That.Will.Throw));