使用自定义解析程序跳过空值

时间:2013-11-16 17:43:25

标签: c# automapper

我想使用automapper在我的公共数据协定和我的数据库模型之间进行映射。我创建了一个自动完成所有契约的类创建映射。我唯一的问题是,如果值不为null,我只想将合约中的值映射到数据库模型。我在这里看了其他问题但是看不到使用自定义解析器的示例。

以下是我的一些代码

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;
   }
}

这就是我想用它的方式

AutoMapper.Mapper.Map(dataContract, dbModel)

这当前工作得很好但是如果dataContract中有一个NULL值,那么它将替换dbModel中的现有值,这不是我想要的。如何使AutoMapper忽略空源值?

修改

正如其中一个答案所指出的那样有

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

除非无法从

访问.ForAllMembers,否则这将是理想的
Mapper.CreateMap(SourceType, DestinationType)

4 个答案:

答案 0 :(得分:30)

更新:IsSourceValueNull为not available starting from V5

如果要忽略所有具有空值的源属性,可以使用:

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

否则,您可以为每个成员执行类似的操作。

阅读this

答案 1 :(得分:15)

对于使用Instance API的较新版本,请改用:

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

注意:此功能从5.0.2开始工作,在撰写本文时打破了更高版本。如果从5.0.2升级,建议等待下一个5.2.x版本。

答案 2 :(得分:5)

解决方案here适用于我的项目,该项目使用的是AutoMapper 6.0.2。在以前使用AutoMapper 4的项目中,我使用了IsSourceValueNull来实现类似的行为。我在项目中将可空类型映射到非可空类型,此解决方案能够处理该场景。

我对原始解决方案做了一些小改动。我没有检查要映射的属性的类型,而是在ForAllPropertyMaps中设置过滤器以检查源对象的类型,以便自定义解析器仅应用于该源对象的映射。但是过滤器可以根据需要设置为任何东西。

var config = new MapperConfiguration(cfg =>
{
    cfg.ForAllPropertyMaps(
        pm => pm.TypeMap.SourceType == typeof(<class of source object>),
        (pm, c) => c.ResolveUsing<object, object, object, object>(new IgnoreNullResolver(), pm.SourceMember.Name));
});

class IgnoreNullResolver : IMemberValueResolver<object, object, object, object>
{
    public object Resolve(object source, object destination, object sourceMember, object destinationMember, ResolutionContext context)
    {
        return sourceMember ?? destinationMember;
    }
}

答案 3 :(得分:2)

我将条件映射到非泛型类型时遇到了同样的问题。这是我解决它的方式。

接线:

foreach (PropertyInfo p in type.GetProperties().Where(x => x.GetCustomAttributes<SkipMapIfNullAttribute>().Any()))
    map.ForMember(p.Name, x => x.ResolveUsing(typeof(SkipMapIfNullResolver)).FromMember(p.Name));

第二个.FromMember是必需的,因此成员的值将传递给值解析器,而不是完整模型。

解析器看起来像这样:

public class SkipMapIfNullResolver : IValueResolver
{
    public ResolutionResult Resolve(ResolutionResult source)
    {
        if (source.Value == null)
            source.ShouldIgnore = true;

        return source;
    }
}