使用automapper映射异步结果

时间:2015-09-16 13:12:10

标签: c# async-await automapper

我们正在创建一个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。我们应该如何配置映射?

4 个答案:

答案 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);            
                }
            );
        }

我很想听听您的想法/评论。