Skip Null Values with Custom Resolver

Skip null values with custom resolver

UPDATE: IsSourceValueNull is not available starting from V5.

If you want all source properties with null values to be ignored you could use:

Mapper.CreateMap<SourceType, DestinationType>()
.ForAllMembers(opt => opt.Condition(srs => !srs.IsSourceValueNull));

Otherwise, you can do something similar for each member.

Read this.

How to ignore null values for all source members during mapping in Automapper 6?

Method Condition now has five overloads, one of which accepts predicate of type

Func<TSource, TDestination, TMember, bool>

this TMember parameter is the source member. So you can check source member for null:

CreateMap<StatusLevelDTO, StatusLevel>()
.ForAllMembers(opts => opts.Condition((src, dest, srcMember) => srcMember != null));

AutoMapper configure to ignore null source member to custom struct type destination member

In AutoMapper, when a destination object is created using service locator, it treats the initial destination as null, and maps all the members using the default value default(MyStruct) when UseDestinationValue is not configured for the member, as written in the source code.

The solution would be to configure .UseDestinationValue() on the member mapping expression.

In my case, there are many models having members that should be mapped from decimal? to MyStruct, so I have an extension method instead.

public static IMappingExpression<TSource, TDestination> UseDestinationValueForKnownMemberTypes<TSource, TDestination>(this IMappingExpression<TSource, TDestination> mappingExpression)
{
mappingExpression.ForAllMembers(options =>
{
if (IsKnownType(options.DestinationMember))
{
options.UseDestinationValue();
}
});
return mappingExpression;
}

private static bool IsKnownType(MemberInfo destinationMember)
{
var propertyType = destinationMember is PropertyInfo p ? p.PropertyType : null;
// Use a static hashset to handle multiple types
return propertyType == typeof(MyStruct);
}

CreateMap<Source, IDestination>()
.UseDestinationValueForKnownMemberTypes();

How to configure Automapper to ignore Object-Properties if properties is null but map if not null

You can specify a custom resolver to explicitly do your custom mapping.

    CreateMap<PersonDTO, Person>()
.ForMember(dest => dest.UserDetails, opt => opt.MapFrom<CustomResolver>());

public class CustomResolver : IValueResolver<PersonDTO, Person, UserProperties>
{
public UserProperties Resolve(PersonDTO source, Person destination, UserProperties member, ResolutionContext context)
{
if (source.UserId == 0)
return null;
return new UserProperties
{
DisplayName = source.UserName,
Id = source.UserId
};
}
}

AutoMapper.Map ignore all Null value properties from source object

Interesting, but your original attempt should be the way to go. Below test is green:

using AutoMapper;
using NUnit.Framework;

namespace Tests.UI
{
[TestFixture]
class AutomapperTests
{

public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int? Foo { get; set; }
}

[Test]
public void TestNullIgnore()
{
Mapper.CreateMap<Person, Person>()
.ForAllMembers(opt => opt.Condition(srs => !srs.IsSourceValueNull));

var sourcePerson = new Person
{
FirstName = "Bill",
LastName = "Gates",
Foo = null
};
var destinationPerson = new Person
{
FirstName = "",
LastName = "",
Foo = 1
};
Mapper.Map(sourcePerson, destinationPerson);

Assert.That(destinationPerson,Is.Not.Null);
Assert.That(destinationPerson.Foo,Is.EqualTo(1));
}
}
}

Automapper - Make IncludeMembers() ignore null

Found possible solutions.

Option 1. IValueResolver

We can create custom value resolver. This solution is good for small amount of fields that we need to map from child objects. As any field will require specific value resolver.

Let's create TitleResolver:

public class TitleResolver : IValueResolver<Item, ItemDetail, string>
{
public string Resolve(Item source, ItemDetail destination, string destMember, ResolutionContext context)
{
if (source != null)
{
if (source.BookMetadata != null)
{
return source.BookMetadata.Title;
}
if (source.MovieMetadata != null)
{
return source.MovieMetadata.Title;
}
}
return null;
}
}

And update ItemProfile:

public class ItemProfile : Profile
{
public ItemProfile()
{
CreateMap<Item, ItemDetail>()
.ForMember(dest => dest.Title, opt => opt.MapFrom<TitleResolver>());
}
}

Link to whole code sample: https://dotnetfiddle.net/6pfKYh

Option 2. BeforeMap()/AfterMap()

In case if our child objects have more than one field to be mapped to destination object it may be a good idea to use BeforeMap() or AfterMap() methods.
In that case ItemProfile will be updated to:

public class ItemProfile : Profile
{
public ItemProfile()
{
CreateMap<Item, ItemDetail>(MemberList.None)
.AfterMap((src, dest, ctx) =>
{
if (src.BookMetadata != null)
{
ctx.Mapper.Map(src.BookMetadata, dest);
}
else if (src.MovieMetadata != null)
{
ctx.Mapper.Map(src.MovieMetadata, dest);
}
});

CreateMap<BookMetadata, ItemDetail>();

CreateMap<MovieMetadata, ItemDetail>();
}
}

Link to whole code sample: https://dotnetfiddle.net/ny1yRU



Related Topics



Leave a reply



Submit