我们正在创建一个angularjs应用程序的Web.Api应用程序。 Web.Api返回一个json结果。
第一步是获取数据:
public List<DataItem>> GetData()
{
return Mapper.Map<List<DataItem>>(dataRepository.GetData());
}
这就像一个魅力。然后我们使数据repo异步,我们改为代码以使用它。
public List<DataItem>> GetData()
{
return Mapper.Map<List<DataItem>>(dataRepository.GetDataAsync().Result);
}
没有问题。现在我们想让我的Web.Api完全异步,因此我们将其更改为:
public async Task<List<DataItem>> GetData()
{
return await Mapper.Map<Task<List<DataItem>>>(dataRepository.GetDataAsync());
}
此时Automapper感到困惑。首先,我们有以下映射器规则: Mapper.CreateMap();
这一直有效,直到web api方法变为完全异步。该例外表示它缺少了
的地图 Task<ICollection<Data>> to Task<ICollection<DataItem>>.
映射器已更改为
Mapper.CreateMap<Task<List<Data>>, Task<List<DataItem>>>();
验证配置时,它会抱怨无法映射Result。我们应该如何配置映射?
答案 0 :(得分:22)
您需要将异步数据提取移出Map呼叫:
var data = await dataRepository.GetDataAsync();
return Mapper.Map<List<DataItem>>(data);
或者,您可以使用AutoMapper LINQ投影:
var data = await dbContext.Data.ProjectTo<DataItem>().ToListAsync();
我不知道您的存储库是否直接暴露了IQueryable(我们不使用存储库)。我们的应用程序现在几乎只使用第二个版本。
答案 1 :(得分:2)
您还可以添加一些可以为您执行此操作的任务扩展方法:
public static Task<TReturn> Convert<T, TReturn>(this Task<T> task)
{
if (task == null)
throw new ArgumentNullException(nameof(task));
var tcs = new TaskCompletionSource<TReturn>();
task.ContinueWith(t => tcs.TrySetCanceled(), TaskContinuationOptions.OnlyOnCanceled);
task.ContinueWith(t =>
{
tcs.TrySetResult(Mapper.Map<T, TReturn>(t.Result));
}, TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(t => tcs.TrySetException(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
return tcs.Task;
}
public static Task<List<TReturn>> ConvertEach<T, TReturn>(this Task<List<T>> task)
{
if (task == null)
throw new ArgumentNullException(nameof(task));
var tcs = new TaskCompletionSource<List<TReturn>>();
task.ContinueWith(t => tcs.TrySetCanceled(), TaskContinuationOptions.OnlyOnCanceled);
task.ContinueWith(t =>
{
tcs.TrySetResult(t.Result.Select(Mapper.Map<T, TReturn>).ToList());
}, TaskContinuationOptions.OnlyOnRanToCompletion);
task.ContinueWith(t => tcs.TrySetException(t.Exception), TaskContinuationOptions.OnlyOnFaulted);
return tcs.Task;
}
答案 2 :(得分:1)
事实证明,乔斯的解决方案对我很有用。但是,我确实将扩展方法重新定位为扩展IMapper,这使我可以删除单例Mapper参考。
public static class MapperExtensions
{
public static Task<TResult> MapAsync<TSource, TResult>(this IMapper mapper, Task<TSource> task)
{
if (task == null)
{
throw new ArgumentNullException(nameof(task));
}
var tcs = new TaskCompletionSource<TResult>();
task
.ContinueWith(t => tcs.TrySetCanceled(), TaskContinuationOptions.OnlyOnCanceled);
task
.ContinueWith
(
t =>
{
tcs.TrySetResult(mapper.Map<TSource, TResult>(t.Result));
},
TaskContinuationOptions.OnlyOnRanToCompletion
);
task
.ContinueWith
(
t => tcs.TrySetException(t.Exception),
TaskContinuationOptions.OnlyOnFaulted
);
return tcs.Task;
}
}
答案 3 :(得分:0)
我最近也遇到了这个问题。我想保留方法的异步部分,因此,我最终得到了一个将 await 操作包装在异步任务中的解决方案。
public async Task<ActionResult<ProjectDTO>> GetProject(long id)
{
return await Task<ProjectDTO>.Run(
async () => {
var project = await _context.MDb.FindAsync(id);
return _mapper.Map<ProjectDTO>(project);
}
);
}
我很想听听您的想法/评论。