'Call async method during automapper mapping

I'm trying to learn automapper (version 10.0.0) and I'm setting up my DTO input object to become my Entity Framework model object, so I did this:

CreateMap<RequestInputDTO, Request>()
    .ForMember(x => x.RequestedById, x => x.Ignore())
    .ForMember(x => x.RequestedForId, x => x.Ignore())
    .ForMember(x => x.OrgHierarchyId, x => x.Ignore())
    .AfterMap((src, dst) => {
        dst.Uuid = Guid.NewGuid();
        dst.RequestedUtc = DateTime.UtcNow;
    });

And so far that part is working fine. However, for those three properties I told it to ignore, I have to do database lookups. They send me a name for example, I grab the ID of the corresponding record and then use that for the ID. So when I receive the request I'm doing this:

var request = _mapper.Map<Request>(dto);
request.RequestedById = await WwidToIdAsync(dto.RequestedByWwid);
request.OrgHierarchyId = await OrgHierarchyNumberToIdAsync(dto.RequestedForOrgHierarchyCode);
request.RequestedForId = await WwidToIdAsync(dto.RequestedForWwid);

While that seems to be doing the right thing, I don't like that I've now disconnected those three items from the automapping. It'd be too easy for someone to forget to do it. I can't figure out how to get the three lines handled automatically during the mapping since AfterMap isn't async.



Solution 1:[1]

Had the same issue with .NET 6.0. I don't know if you had the issue on Core or prior versions, but here's a solution (or maybe workaround) for AutoMapper 11 and .NET 6.0 (can be applied to any Core version, because it is using dependency injection).

  1. I've added a new service where mentioned async methods are located:
    // Input and output parameter types were defined based on guessing from names :)
    public interface IMyService
    {
        Task<bool> WwidToIdAsync(bool requestedByWwid);
        Task<int> OrgHierarchyNumberToIdAsync(bool requestedForOrgHierarchyCode);
    }    

    public class MyService : IMyService
    {
        public async Task<bool> WwidToIdAsync(bool requestedByWwid)
        {
            // Your db lookup here
        }
    
        public async Task<int> OrgHierarchyNumberToIdAsync(bool 
                     requestedForOrgHierarchyCode)
        {
            // Another db lookup here
        }
    }
  1. Register the service above in dependency container:
    services.AddScoped<IMyService, MyService>();
  1. Here's where all the magic happens: creating new MappingAction and injecting IMyService to it.
public class RequestInputMappingAction : IMappingAction<RequestInputDTO, Request>
{
    private readonly IMyService _myService;

    public RequestInputMappingAction(IMyService myService)
    {
        _myService = myService;
    }

    public void Process(RequestInputDTO source, Request destination, ResolutionContext context)
    {
        destination.Uuid = Guid.NewGuid();
        destination.RequestedUtc = DateTime.UtcNow;
        destination.RequestedById = _myService.WwidToIdAsync(source.RequestedByWwid).GetAwaiter().GetResult();
        destination.OrgHierarchyId = _myService.OrgHierarchyNumberToIdAsync(source.RequestedForOrgHierarchyCode).GetAwaiter().GetResult();
        destination.RequestedForId = _myService.WwidToIdAsync(source.RequestedForWwid).GetAwaiter().GetResult();
    }
}
  1. And the final step - CreateMap
CreateMap<RequestInputDTO, Request>()
            .AfterMap<RequestInputMappingAction>();

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1