2

I am looking to map from the list of objects to a class with booleans. I have the following structure from the source:

public partial class HomeInfoBuilding : object, System.ComponentModel.INotifyPropertyChanged
{

    public HomeInfoBuildingEntry[] appliedDiscountsField;
}

public partial class HomeInfoBuildingEntry : object, System.ComponentModel.INotifyPropertyChanged
{

    private string discountTypeField;

    private System.Nullable<bool> discountValueField;

    private bool discountValueFieldSpecified;

    private string actionField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(IsNullable = true, Order = 0)]
    public string DiscountType
    {
        get
        {
            return this.discountTypeField;
        }
        set
        {
            this.discountTypeField = value;
            this.RaisePropertyChanged("DiscountType");
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(IsNullable = true, Order = 1)]
    public System.Nullable<bool> DiscountValue
    {
        get
        {
            return this.discountValueField;
        }
        set
        {
            this.discountValueField = value;
            this.RaisePropertyChanged("DiscountValue");
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public bool DiscountValueSpecified
    {
        get
        {
            return this.discountValueFieldSpecified;
        }
        set
        {
            this.discountValueFieldSpecified = value;
            this.RaisePropertyChanged("DiscountValueSpecified");
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string action
    {
        get
        {
            return this.actionField;
        }
        set
        {
            this.actionField = value;
            this.RaisePropertyChanged("action");
        }
    }

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

    protected void RaisePropertyChanged(string propertyName)
    {
        System.ComponentModel.PropertyChangedEventHandler propertyChanged = this.PropertyChanged;
        if ((propertyChanged != null))
        {
            propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }

}

Destination:

public class AppliedDiscountInfo
{
    public System.Nullable<bool> AgeDiscount { get; set; }

    public System.Nullable<bool> AlarmDiscount { get; set; }

    public System.Nullable<bool> BuildingTypeDiscount { get; set; }

    public System.Nullable<bool> CombinedDiscount { get; set; }

    public System.Nullable<bool> DriverExclBonusDiscount { get; set; }

    public System.Nullable<bool> HouseholdExcellenceBonusDiscount { get; set; }

    public System.Nullable<bool> MultipleProductDiscount { get; set; }

    public System.Nullable<bool> MultiPetDiscount { get; set; }

    public System.Nullable<bool> NoClaimDiscount { get; set; }

    public System.Nullable<bool> RoadSideAssistanceLoyaltyDiscount { get; set; }

    public System.Nullable<bool> SeniorCardHolderDiscount { get; set; }
}

I want the mapping to be done from the array of appliedDiscountsField to AppliedDiscountInfo as per below:

s.appliedDiscountsField.any(x => x.DiscountType == "AgeDiscount" && x.DiscountValue) => d.AgeDiscount 

So basically if the source array contains a particular discount then the relevant bool in the destination should be set to true.

2
  • From what I see, you can implement a Type Converter with your following logic to check based on the source's DiscountType and DiscountValue and perform mapping to destination member. Commented Sep 11, 2024 at 23:53
  • I am concerned that this seems to duplicate the code N times with different DiscountType values. Next time when you need to maintain the discount type, you need to modify both AppliedDiscountInfo class and the type converter. I am not sure that you are accepting this kind of answer or looking for some reflection way that you only need to maintain AppliedDiscountInfo class without affecting the logic in the converter. Commented Sep 11, 2024 at 23:54

1 Answer 1

3

As mentioned in the comment, you should look for the Type Converter and for each property to assign the value based on the existence of the record matching DiscountType and DiscountValue element in the source's appliedDiscountsField.

public class AppliedDiscountInfoConverter : ITypeConverter<HomeInfoBuilding, AppliedDiscountInfo>
{
    public AppliedDiscountInfo Convert(HomeInfoBuilding source, AppliedDiscountInfo destination, ResolutionContext context)
    {
        AppliedDiscountInfo appliedDiscountInfo = new()
        {
            AgeDiscount = source.appliedDiscountsField.Any(x => x.DiscountType == nameof(AppliedDiscountInfo.AgeDiscount) 
                && x.DiscountValue.GetValueOrDefault()),
            AlarmDiscount = source.appliedDiscountsField.Any(x => x.DiscountType == nameof(AppliedDiscountInfo.AlarmDiscount) 
                && x.DiscountValue.GetValueOrDefault()),
            BuildingTypeDiscount = source.appliedDiscountsField.Any(x => x.DiscountType == nameof(AppliedDiscountInfo.BuildingTypeDiscount) 
                && x.DiscountValue.GetValueOrDefault()),
            CombinedDiscount = source.appliedDiscountsField.Any(x => x.DiscountType == nameof(AppliedDiscountInfo.CombinedDiscount) 
                && x.DiscountValue.GetValueOrDefault()),
            DriverExclBonusDiscount = source.appliedDiscountsField.Any(x => x.DiscountType == nameof(AppliedDiscountInfo.DriverExclBonusDiscount) 
                && x.DiscountValue.GetValueOrDefault()),
            HouseholdExcellenceBonusDiscount = source.appliedDiscountsField.Any(x => x.DiscountType == nameof(AppliedDiscountInfo.HouseholdExcellenceBonusDiscount) 
                && x.DiscountValue.GetValueOrDefault()),
            MultipleProductDiscount = source.appliedDiscountsField.Any(x => x.DiscountType == nameof(AppliedDiscountInfo.MultipleProductDiscount) 
                && x.DiscountValue.GetValueOrDefault()),
            MultiPetDiscount = source.appliedDiscountsField.Any(x => x.DiscountType == nameof(AppliedDiscountInfo.MultiPetDiscount) 
                && x.DiscountValue.GetValueOrDefault()),
            NoClaimDiscount = source.appliedDiscountsField.Any(x => x.DiscountType == nameof(AppliedDiscountInfo.NoClaimDiscount) 
                && x.DiscountValue.GetValueOrDefault()),
            RoadSideAssistanceLoyaltyDiscount = source.appliedDiscountsField.Any(x => x.DiscountType == nameof(AppliedDiscountInfo.RoadSideAssistanceLoyaltyDiscount) 
                && x.DiscountValue.GetValueOrDefault()),
            SeniorCardHolderDiscount = source.appliedDiscountsField.Any(x => x.DiscountType == nameof(AppliedDiscountInfo.AgeDiscount) 
                && x.DiscountValue.GetValueOrDefault()),
        };
            
        return appliedDiscountInfo;
    }
}

However, this sound duplicates the logic with different values multiple times. Adding that in the future you need to maintain the AppliedDiscountInfo class by adding/renaming/removing the discount type, you also need to maintain the converter.

Hence, you may look for handling the logic with System.Reflection as below:

using System.Reflection;

AppliedDiscountInfo appliedDiscountInfo = new();
        
PropertyInfo[] propertyInfos = typeof(AppliedDiscountInfo).GetProperties(BindingFlags.Public | BindingFlags.Instance)
    .Where(x => x.PropertyType == typeof(bool) || x.PropertyType == typeof(System.Nullable<bool>))
    .ToArray();
            
foreach (var propInfo in propertyInfos)
{
    propInfo.SetValue(appliedDiscountInfo, source.appliedDiscountsField.Any(x => x.DiscountType == propInfo.Name 
        && x.DiscountValue.GetValueOrDefault()));
}
            
return appliedDiscountInfo;

And not forget to register the map with the type converter in the Mapping Profile/Configuration:

cfg.CreateMap<HomeInfoBuilding, AppliedDiscountInfo>()
    .ConvertUsing<AppliedDiscountInfoConverter>();

As suggested by @LucianBargaoanu, the logic can be simplified with ForAllMembers.

cfg.CreateMap<HomeInfoBuilding, AppliedDiscountInfo>()
    .ForAllMembers(o => o.MapFrom((src, dest, value) => src.appliedDiscountsField
        .Any(x => x.DiscountType == o.DestinationMember.Name 
            && x.DiscountValue.GetValueOrDefault())
    ));
Sign up to request clarification or add additional context in comments.

4 Comments

I will try this one.
You have access to the destination member in ForAllMembers so you could use a reflection based approach there. That should simpify things a bit.
Hi @LucianBargaoanu, yeah this is simpler than my original way as this demo. Thanks for the suggestion. 💗

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.