pedrommuller
pedrommuller

Reputation: 16066

Automapper Many to one map configuration

I want to map 3 different classes into a single DTO, each property have the same name on the source and the destination, the classes are the following:

this is the DTO and how I want to map my objects:

public class CandidateTextInfo
    {
        public string ProfilePicture { get; set; }   //-->User
        public ObjectId UserId { get; set; }         //-->User
        public string Name { get; set; }             //--> Candidate
        public string Headline { get; set; }         //--> Candidate
        public Gender Gender { get; set; }           //--> Candidate
        public byte Rating { get; set; }             //--> Candidate
        public bool IsCompany { get; set; }          //--> Candidate
        public string[] Tags { get; set; }           //--> Portafolio
        public string[] Categories { get; set; }     //--> Portafolio
        public string ExecutiveSummary { get; set; } //--> Portafolio
        public HourlyRate HourlyRate{ get; set; }    //--> Candidate
    }

I've been looking in SO and I found this solution but I don't get the method ConstructUsing

so how can I do to have a many to one mapping, is that possible, if not any workaround?

Upvotes: 0

Views: 2985

Answers (2)

Iain Galloway
Iain Galloway

Reputation: 19210

It depends greatly on the relationships between your objects. If you have a 1:1 relationship between your objects (e.g. if User has properties User.Candidate and User.Portfolio) then the mapping is easy:-

CreateMap<User, CandidateTextInfo>()
  .ForMember(d => d.ProfilePicture, o => o.MapFrom(s => s.ProfilePicture)
  // ...
  .ForMember(d => d.Name, o => o.MapFrom(s => s.Candidate.Name)
  // And so on...

If you don't have a one-to-one mapping, you need to arrange things a little bit yourself:-

public class CandidateTextInfoSource
{
  public CandidateTextInfoSource(User user,
                                 Candidate candidate,
                                 Portafolio portafolio)
  {
    this.User = user;
    this.Candidate = candidate;
    this.Portafolio = portafolio;
  }

  public User User { get; set; }
  public Candidate Candidate { get; set; }
  public Portafolio Portafolio { get; set; }
}

// ...

CreateMap<CandidateTextInfoSource, CandidateTextInfo>()
  .ForMember(d => d.ProfilePicture, o => o.MapFrom(s => s.User.ProfilePicture)
   // ...
  .ForMember(d => d.Name, o => o.MapFrom(s => s.Candidate.Name)
   // And so on...

You can then use whatever means you require to create your CandidateTextInfoSource depending on the relationship between your objects. For example, if I assume that a User has a collection User.Candidates, and a Candidate has a property Candidate.Portfolio:-

CreateMap<User, IEnuemerable<CandidateTextInfoSource>>()
  .ConstructUsing(
    x => x.Candidates
          .Select(y => Mapper.Map<CandidateTextInfo>(new CandidateTextInfoSource(x, y, y.Portfolio)))
  .ToList());

I appreciate that this answer is very late, but if you further specify the relationship between your objects, I can help you create a more specific mapping.

Upvotes: 1

samy
samy

Reputation: 14972

Automapper's ConstructUsing is useful to build one property from custom code. In your case it is not really necessary. You just need to create the maps from your objects to your DTO. Then map each object instance to the same DTO instance.

However since Automapper wants each property of the destination object to be defined in order to ensure that the destination is fully specified you will need to configure each mapping with the properties not existing in the source object as ignored

CreateMap<Candidate, CandidateTextInfo>()
.ForMember(x=> x.ProfilePicture, opt => opt.Ignore())
.ForMember(... 
// repeat for all destination properties not existing in source properties

If this is too much boilerplate code, many solutions are explored on stack overflow, among which this one looks promising: AutoMapper: "Ignore the rest"? (look at Robert Schroeder's answer)

Upvotes: 0

Related Questions