Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Skip null values with custom resolver

Tags:

c#

automapper

I want to use automapper to map between my public data contracts and my DB models. I have created a class which automatically goes through all the contracts are creates mappings. The only problem I have is that I only want to map values from the contract to the DB model if the value is not null. I have looked at other question on here but cant see examples that use custom resolvers.

Here is some of my code

var mapToTarget = AutoMapper.Mapper.CreateMap(contract, mappedTo);
foreach (var property in contract.GetProperties().Where(property => property.CustomAttributes.Any(a => a.AttributeType == typeof(MapsToProperty))))
{
  var attribute = property.GetCustomAttributes(typeof(MapsToProperty), true).FirstOrDefault() as MapsToProperty;

  if (attribute == null) continue;

  mapToTarget.ForMember(attribute.MappedToName,
                    opt => 
                        opt.ResolveUsing<ContractToSourceResolver>()
                            .ConstructedBy(() => new ContractToSourceResolver(new MapsToProperty(property.Name, attribute.SourceToContractMethod, attribute.ContractToSourceMethod))));
}


private class ContractToSourceResolver : ValueResolver<IDataContract, object>
{
  private MapsToProperty Property { get; set; }

  public ContractToSourceResolver(MapsToProperty property)
  {
     Property = property;
  }

  protected override object ResolveCore(IDataContract contract)
  {
     object result = null;
     if (Property.ContractToSourceMethod != null)
     {
         var method = contract.GetType()
                    .GetMethod(Property.ContractToSourceMethod, BindingFlags.Public | BindingFlags.Static);
          result = method != null ? method.Invoke(null, new object[] {contract}) : null;
      }
      else
      {
         var property =
                    contract.GetType().GetProperties().FirstOrDefault(p => p.Name == Property.MappedToName);
         if (property != null)
         {
             result = property.GetValue(contract);
         }
      }

      return result;
   }
}

And this is how I want to use it

AutoMapper.Mapper.Map(dataContract, dbModel)

This currently works great but if there is a NULL value in the dataContract then it will replace the existing value in the dbModel, this is not what I want. How do I make AutoMapper ignore null source values?

EDIT

As pointed out in one of the answers there is this

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

This would be ideal except for the fact that .ForAllMembers is not accessible from

Mapper.CreateMap(SourceType, DestinationType)
like image 677
Mike Norgate Avatar asked Nov 16 '13 17:11

Mike Norgate


2 Answers

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.

like image 89
Prasad Kanaparthi Avatar answered Sep 30 '22 01:09

Prasad Kanaparthi


For newer versions that utilize the Instance API, use this instead:

var mappingConfig = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<SourceType, DestinationType>().ForAllMembers(opt => opt.Condition(
        (source, destination, sourceMember, destMember) => (sourceMember != null)));
});

Note: This functionality works as of 5.0.2, breaking on later versions as of this writing. Waiting for the next 5.2.x release is recommended if upgrading from 5.0.2.

like image 38
Marc Lopez Avatar answered Sep 30 '22 01:09

Marc Lopez