void Main() { var configurationStore = new ConfigurationStore(new TypeMapFactory(), MapperRegistry.Mappers); var mapper = new MappingEngine(configurationStore); var server = new Server(); configurationStore .CreateMap() .ForMember(d => d.ServerThing, m => m.ResolveUsing(new FromServer1Resolver(server)).FromMember(s => s.ServerThingId)) .ForMember(d => d.AnotherServerThing, m => m.ResolveUsing(new FromServer2Resolver(server)).FromMember(s => s.AnotherServerThingId)); var fromValue = new From { ServerThingId = "Id1", AnotherServerThingId = "Id2" }.Dump(); mapper.AsyncMap(fromValue).Result.Dump(); } public static class AsyncExtensions { public static Task AsyncMap(this IMappingEngine mapper, object source) { var asyncMapContext = new AsyncContext(); // TODO the exception should be inside the task var result = mapper.Map(source, o => o.Items.Add("AsyncContext", asyncMapContext)); return asyncMapContext.MappingTask.ContinueWith(t => result); } } public class Server { public Task GetThing(string id) { return Task.Run(() => new FromServer { Id = id, OtherData = Guid.NewGuid().ToString() }); } public Task GetAnotherThing(string id) { return Task.Run(() => new FromServer2 { Id = id, OtherData = Guid.NewGuid().ToString() }); } } public class FromServer1Resolver : AsyncValueResolver { Server server; public FromServer1Resolver(Server server) { this.server = server; } protected override Task GetValue(string fromValue) { return server.GetThing(fromValue); } } public class FromServer2Resolver : AsyncValueResolver { Server server; public FromServer2Resolver(Server server) { this.server = server; } protected override Task GetValue(string fromValue) { return server.GetAnotherThing(fromValue); } } public abstract class AsyncValueResolver : IValueResolver { public ResolutionResult Resolve(ResolutionResult source) { var contextItems = source.Context.Options.Items; if (!contextItems.ContainsKey("AsyncContext")) throw new InvalidOperationException("You must use mapper.AsyncMap when using async value resolvers"); var asyncContext = (AsyncContext)contextItems["AsyncContext"]; asyncContext.StartAsyncOperation(); var parentSourceValue = source.Context.SourceValue; var sourceValue = source.Context.SourceType.GetProperty(source.Context.PropertyMap.SourceMember.Name).GetValue(source.Context.SourceValue); GetValue((TFrom)source.Value) .ContinueWith(r => { source.Context.PropertyMap.DestinationProperty.SetValue(source.Context.DestinationValue, r.Result); asyncContext.OperationFinished(); }); return source.Ignore(); } protected abstract Task GetValue(TFrom fromValue); } public class AsyncContext { object locker = new object(); TaskCompletionSource taskSource = new TaskCompletionSource(); int activeCalls = 0; public void StartAsyncOperation() { lock (locker) { activeCalls++; }; } public void OperationFinished() { var isComplete = false; lock (locker) { activeCalls--; if (activeCalls == 0) { isComplete = true; } }; if (isComplete) taskSource.SetResult(null); } public Task MappingTask { get { return taskSource.Task; } } } public class To { public FromServer ServerThing { get; set; } public FromServer2 AnotherServerThing { get; set; } } public class From { public string ServerThingId {get;set;} public string AnotherServerThingId {get;set;} } public class FromServer { public string Id { get; set; } public string OtherData { get; set; } } public class FromServer2 { public string Id { get; set; } public string OtherData { get; set; } }