How do I use AutoMapper to map via a constructor as well as resolving injected services

Viewed 685

I am trying to use AutoMapper to map between two types where the destination type needs to be created from a constructor rather than a property mapping.

The complication is that whilst one of the construction parameters is a value I want to map from the source, the second is a service that I want Automapper to resolve using the configured DI container.

I have tried using the recommended .ForCtorParam method but if I only specify the first parameter I get a runtime error of:

"Destination needs to have a constructor with 0 args or only optional args

I thought I might need to specify both parameters but I can't see how I could instruct the mapper to resolve the service.

Below details an example of the type of class structures I am using and how the mapper has been configured.

public class Source
{
   public string Value {get;set;}
}

public class Destination
{
    private IService _someService;

    public Destination(string value, IService someService)
    {
        Value = value;
        _someService = someService;
    }
    
    public string Value {get;}
}

public static class MapperFactory
{
   public static IMapper CreateMapper(IUnitContainer container)
   {
      var config = new MapperConfiguration(cfg => 
      {
          cfg.ConstructServicesUsing(type => container.Resolve(type));
     
          cfg.CreateMap<Source,Destination>().ForCtorParam("value", opt => opt.MapFrom(s => s.Value));
      });

      return config.CreateMapper();
    }
}
1 Answers

Do you have to use a constructor? Every case where I've used automapper, it's been to a class without a constructor, and if particular properties need particular values, then I've gone with this approach (using your example):

cfg.CreateMap<Source,Destination>()
    .ForMember(dest => dest.Value, opt => opt.MapFrom(src => src.Value);

and if you have specific hard coded values that need to be passed in through constructors then you just use opt => opt.UseValue("hard-coded")

have you got a specific example of a class that you deem needs to be made via a constructor?

Related