How can you map a property to a sub-property that may be null?
eg the following code will fail with a NullReferenceException because the Contact's User property is 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; }
}
}
I'd like ContactModel's U开发者_C百科serName to default to an empty string instead.
I have tried the NullSubstitute method, but I assume that's trying to operate with User.Username, rather than just on the User property.
You could write the mapping code like follows:
Mapper.CreateMap<Contact, ContactModel>()
.ForMember( x => x.UserName, opt => opt.MapFrom( y => (y.User != null) ? y.User.UserName : "" ) );
This will check if the User
is null or not and then assign either an emtpy string or the UserName
.
If you find yourself doing a lot of null checking like in Dave's answer, you might consider applying the technique I've blogged about a while ago: Getting rid of null checks in property chains. This will allow you to write this:
Mapper.CreateMap<Contact, ContactModel>()
.ForMember(x => x.UserName,
opt => opt.NullSafeMapFrom(y => y.User.UserName) ?? string.Empty);
A solution I've used is to create a closure around the original delegate, which wraps it in a try/catch block. It's unfortunately necessary to use Expression.Compile()
to stop Visual Studio from catching the exception when it's thrown in the original delegate. Probably not recommended in high performance environments, but I've never had any issue using it in regular UI stuff. Your milage may vary.
Extension method
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);
});
}
}
Usage
.ForMember(dest => dest.Target, opt => opt.NullSafeMapFrom(src => src.Something.That.Will.Throw));
精彩评论