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
.
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())
));