AutoMapper 按条件将一个 属性 映射到两个属性

AutoMapper Mapping one property to two properties by condition

有两个classes

        class A
        {
            public string ProductionDivision { get; set; }
        }


        class B
        {
            private object _productionDivision;

            public enum ProductionDivisionOneofCase
            {
                None = 0,
                IsNullproductionDivision = 15,
                ProductionDivisionValue = 16,
            }
            private ProductionDivisionOneofCase _productionDivisionCase = ProductionDivisionOneofCase.None;


            public bool IsNullProductionDivision
            {
                get { return _productionDivisionCase == ProductionDivisionOneofCase.IsNullproductionDivision ? (bool)_productionDivision : false; }
                set
                {
                    _productionDivision = value;
                    _productionDivisionCase = ProductionDivisionOneofCase.IsNullproductionDivision;
                }
            }

            public string ProductionDivisionValue
            {
                get { return _productionDivisionCase == ProductionDivisionOneofCase.ProductionDivisionValue ? (string)_productionDivision : ""; }
                set
                {
                    _productionDivision = value;
                    _productionDivisionCase = ProductionDivisionOneofCase.ProductionDivisionValue;
                }
            }
        }

我想根据条件将 ProductionDivision 属性 映射到 class B 的属性之一 - null(映射到 IsNullProductionDivision ) 或不是源 属性 值的 null(映射到 ProductionDivisionValue)。我可以像下面这样实现它。

CreateMap<A, B>()
               .ForMember(g => g.IsNullProductionDivision, m =>
               {
                   m.PreCondition(s => s.ProductionDivision == null);
                   m.MapFrom(b => true);
               })
               .ForMember(g => g.ProductionDivisionValue, m =>
               {
                   m.PreCondition(s => s.ProductionDivision != null);
                   m.MapFrom(b => b.ProductionDivision);
               });

如果{source 属性 name} 的值为null,则IsNull{source 属性 name} 的值为true。 否则,如果 {source 属性 name} 的值不是 null,则 {source 属性 name} 的值是 {source 属性 name} 的值。

我有很多属性响应此映射规则。所以,我不想像上面那样为每个属性编写映射规则。我想在全球范围内为这种映射配置一个规则。

如何配置 AutoMapper 才能处理如此复杂的映射?

我找到了解决办法。解决方案非常简单明了。结果如下:

完整代码:

public class Program
{

    class A
    {
        public string ProductionDivision { get; set; }
    }


    class B
    {
        private object _productionDivision;

        public enum ProductionDivisionOneofCase
        {
            None = 0,
            IsNullproductionDivision = 15,
            ProductionDivisionValue = 16,
        }
        private ProductionDivisionOneofCase _productionDivisionCase = ProductionDivisionOneofCase.None;


        public bool IsNullProductionDivision
        {
            get { return _productionDivisionCase == ProductionDivisionOneofCase.IsNullproductionDivision ? (bool)_productionDivision : false; }
            set
            {
                _productionDivision = value;
                _productionDivisionCase = ProductionDivisionOneofCase.IsNullproductionDivision;
            }
        }

        public string ProductionDivisionValue
        {
            get { return _productionDivisionCase == ProductionDivisionOneofCase.ProductionDivisionValue ? (string)_productionDivision : ""; }
            set
            {
                _productionDivision = value;
                _productionDivisionCase = ProductionDivisionOneofCase.ProductionDivisionValue;
            }
        }
    }

   public class StrinToBoolCustomResolver
        : IValueConverter<string, bool>
    {
        public bool Convert(string sourceMember, ResolutionContext context)
        {
            return sourceMember == null;
        }
    }

    public class MyAutoMapperProfile
        : Profile
    {
        public MyAutoMapperProfile()
        {
            // add post and pre prefixes to add corresponding properties in the inner property map
            RecognizeDestinationPostfixes("Value");
            RecognizeDestinationPrefixes("IsNull");

            // add mapping for "value" property
            this.ForAllPropertyMaps(map => map.SourceMember.Name + "Value" == map.DestinationName,
                (map, expression) =>
                {
                    expression.Condition((source, dest, sMem, destMem) =>
                    {
                        return sMem != null;
                    });
                    expression.MapFrom(map.SourceMember.Name);
                });

            // add mapping for "IsNull" property
            this.ForAllPropertyMaps(map => "IsNull" + map.SourceMember.Name == map.DestinationName,
                (map, expression) =>
                {
                    expression.Condition((source, dest, sMem, destMem) =>
                    {
                        return (bool)sMem;
                    });
                    //expression.MapFrom(map.SourceMember.Name);
                    expression.ConvertUsing<string, bool>(new StrinToBoolCustomResolver(), map.SourceMember.Name);
                });



            CreateMap<A, B>();
               //.ForMember(g => g.IsNullProductionDivision, m =>
               //{
               //    m.PreCondition(s => s.ProductionDivision == null);
               //    m.MapFrom(b => true);
               //});
               //.ForMember(g => g.ProductionDivisionValue, m =>
               //{
               //    m.PreCondition(s => s.ProductionDivision != null);
               //    m.MapFrom(b => b.ProductionDivision);
               //});
        }
    }


    public static void Main(string[] args)
    {
        var configuration = new MapperConfiguration(cfg => {
            cfg.AddProfile(new MyAutoMapperProfile());
        });

        var mapper = new Mapper(configuration);
        mapper.ConfigurationProvider.AssertConfigurationIsValid();
        var a = new A()
        {
            ProductionDivision = null
        };
        // b._productionDivisionCase  will be equal IsNullproductionDivision 
        var b = mapper.Map<B>(a);

        var c = new A()
        {
            ProductionDivision = "dsd"
        };
        // d._productionDivisionCase  will be equal ProductionDivisionValue
        var d = mapper.Map<B>(c);
    }
}

澄清:

  1. add (post/pre)fixes 将相应的属性添加到内部 属性 映射。它需要在这里做,因为我们的属性应该被 AutoMapper 捕获。否则属性将被放弃并且映射器配置将失败。

  2. 之后,我们配置这些属性需要如何映射。我们调用 ForAllPropertyMaps 方法,过滤所有属性并设置规则以映射适合我们过滤器的属性。创建映射器对象时,将根据指定的过滤器构建执行计划。

  • 对于“值”属性我们添加一个条件来检查源属性是否不为空。如果为空,则映射将丢失。
  • 对于“IsNull”属性 首先我们添加一个转换器,将string类型转换为bool类型。转换器只是将源字符串 属性 与空值进行比较。如果源 属性 等于 null,则转换器 returns 为真。因此,条件接收到一个真值,returns 真,映射将完成。否则转换器 returns false,因此条件 returns false 和映射将丢失。

因此,根据源 属性 是否为空值,源 属性 将映射到不同的目标属性。此外,如果不是必须调用相应目标属性的相应设置方法,则不会调用。