简体   繁体   中英

AutoMapper Mapping one property to two properties by condition

There are two 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;
                }
            }
        }

I would like to map the ProductionDivision property to one of properties of class B depending on the condition - null (map to IsNullProductionDivision ) or not null (map to ProductionDivisionValue ) of the source property value. I can achieve it like as below.

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

If the value of {source property name} is null then the value of IsNull{source property name} is true . Else if the value of {source property name} is not null then the value of {source property name}Value is the value of {source property name}.

I have many properties that respond this mapping rule. So, I don't want to write mapping rule for each properties like above. I want to configurate a rule for such mapping globally.

How can I configure AutoMapper so that it can handle such complex mapping?

I have found solution. The solution is pretty simple and clear. It turns out as follow:

Full code:

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

Clarification:

  1. add (post/pre)fixes to add corresponding properties to inner property map. It need to do here because our properties should be catched by AutoMapper. Otherwise properties will be abandoned and mapper configuration will be failed.

  2. After that, we configurate how these properties need to be mapping. We call ForAllPropertyMaps method, filtering all properties and setting a rule to mapping properties suitable with our filter. When the mapper object is creating, the execution plan will be built taking look the specified filters.

  • For "Value" property we add a condition to check whether the source property is not null. If it is null, then mapping will be missed.
  • For "IsNull" property First of all, we add a converter to convert string type to bool type. The converter just compares the source string property with null value. If the source property equals null, then the converter returns true. So, the condition receives a true value, returns true, and mapping will be done. Otherwise the converter returns false, so the condition returns false and mapping will be missed.

Thus, the mapping of source property will occur to different destination properties depending on whether the source property is null value. Besides that, corresponding set methods of corresponding destination properties will be not called if it not must to be called.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM