Tuesday, December 5, 2017

AutoMapper : Handling Profile Dependencies using Custom Value Resolvers

If you are using or if you have used (I am sure you have) AutoMapper, Profiles lets you organize your mapping configurations in an easy manner.

In this post, let’s see how we can handle AutoMapper Profile dependencies using Custom Value Resolvers. As it’s always good to go with an example, let’s go with an example ASP.NET Core Web Application.

For an ASP.NET Core Web Application, AutoMapper can be configured with following easy steps.
You can add a class deriving from Profile class, and in the constructor you can setup your mapping configurations.
public class SomeProfile : Profile
{
    public SomeProfile()
    {
        CreateMap<MyClass, MyClassDTO>();
        // likewise
    }
}
Next in the Startup.ConfigureServices method, you just need to add the following line. (note: you will need to installer required AutoMapper nuget package)
public void ConfigureServices(IServiceCollection services)
{
    // some code
    services.AddAutoMapper();
}
Now you can use IMapper in your required classes as follows.
public class MyController : Controller
{
    private IMapper _mapper;
 
    public MyController(IMapper mapper)
    {
        _mapper = mapper;
    }
}
Now consider we have following two classes.
public class MyClass
{
    public int Id { get; set; }
}
 
public class MyClassDTO
{
    public int Id { get; set; }

    public string SomeProperty { get; set; }
}
And here on MyClassDTO, SomeProperty can't be mapped directly, we will need to get the value by calling ISomeService.GetSomeProperty(int id). Imagine ISomeService is registered for dependency injection.
public interface ISomeService
{
    string GetSomeProperty(int id);
}
So what we would expect is we can get the MyProperty value as follows.
public class SomeProfile : Profile
{
    private ISomeService _someService;
 
    public SomeProfile(ISomeService someService)
    {
        _someService = someService;
 
        CreateMap<MyClass, MyClassDTO>()
            .ForMember(obj => obj.SomeProperty,
                exp => exp.MapFrom(prop => _someService.GetSomeProperty(prop.Id)));
    }
}
But unfortunately, if we run this, we will get an error “No parameterless constructor defined for this object” on services.AddAutoMapper().

In these kinds of scenarios, we can use AutoMapper Custom Value Resolvers. Here I can do the DI without any issues.
public class MyPropertyResolver : IValueResolver<MyClass, MyClassDTO, string>
{
    private ISomeService _someService;
 
    public MyPropertyResolver(ISomeService someService)
    {
        _someService = someService;
    }
 
    public string Resolve(MyClass source, MyClassDTO destination, string destMember, ResolutionContext context)
    {
        return _someService.GetSomeProperty(source.Id);
    }
}
And the usage would be as follows.
public class SomeProfile : Profile
{
    public SomeProfile()
    {
        CreateMap<MyClass, MyClassDTO>()
            .ForMember(obj => obj.SomeProperty,
                exp => exp.ResolveUsing<MyPropertyResolver>());
    }
}
Now SomeProperty value should get resolved without any errors.

Hope this helps.

Happy Coding.

Regards,
Jaliya