Search code examples
automapperautomapper-6

AutoMapper bi-directional mapping between first item in collection and single property


I'm using AutoMapper 6. Consider the following classes:

public class Source
{
    public FlattenableClass Flattenable { get; set; }

    public List<EmailAddress> EmailAddresses { get; set; }
}

public class Destination
{
    public string FlattenableProp1 { get; set; }

    public string FlattenableProp2 { get; set; }

    public MappedEmailAddress EmailAddress1 { get; set; }
}

where FlattenableClass has properties named Prop1 and Prop2. As you can see, the source has a collection of EmailAddress but the destination only needs the first one because although our database allows a collection of email addresses, the application is going to support one. I believe I can arrange this mapping from Source to Destination like so:

CreateMap<Source, Destination>()
    .ForMember(d => d.EmailAddress1, opt => opt.ResolveUsing(s => s.EmailAddresses?.FirstOrDefault()));

but, unsurprisingly, if I then call ReverseMap() on that, it doesn't know how to map EmailAddress1 back into the EmailAddresses collection. Is there any way to get it to map EmailAddress1 back to an EmailAddress and add it to the EmailAddresses collection?


Solution

  • I haven't found any support in AutoMapper but I found a work around. I just created a target property in the source to do the work for me:

    public class Destination
    {
        public string FlattenableProp1 { get; set; }
    
        public string FlattenableProp2 { get; set; }
    
        public MappedEmailAddress EmailAddress1 { get; set; }
    
        public List<MappedEmailAddress> EmailAddresses
        {
            get
            {
                List<MappedEmailAddress> emails = new List<MappedEmailAddress>();
                if (EmailAddress1 != null) emails.Add(EmailAddress1);
    
                return emails;
            }
            set
            {
                if (value == null || value.Count == 0)
                    EmailAddress1 = new MappedEmailAddress();
                else
                    EmailAddress1 = value[0];
            }
        }
    }
    

    This lets AutoMapper map Source.EmailAddresses to Destination.EmailAddresses and then Destination.EmailAddresses does the work of mapping to EmailAddress1. It's not ideal in that I had to expose collection of email addresses property which I don't want to ever be used by anyone but auto-mapper, but it gets it done.