使用automapper映射异步结果

Pat*_*ick 9 c# automapper async-await

我们正在创建一个angularjs应用程序的Web.Api应用程序.Web.Api返回json结果.

第一步是获取数据:

    public List<DataItem>> GetData()
    {
        return Mapper.Map<List<DataItem>>(dataRepository.GetData());
    }
Run Code Online (Sandbox Code Playgroud)

这就像一个魅力.然后我们使数据repo异步,我们改为代码以使用它.

    public List<DataItem>> GetData()
    {
        return Mapper.Map<List<DataItem>>(dataRepository.GetDataAsync().Result);
    }
Run Code Online (Sandbox Code Playgroud)

Stil没问题.现在我们想让我的Web.Api完全异步,因此我们将其更改为:

    public async Task<List<DataItem>> GetData()
    {
        return await Mapper.Map<Task<List<DataItem>>>(dataRepository.GetDataAsync());
    }
Run Code Online (Sandbox Code Playgroud)

此时Automapper感到困惑.首先,我们有以下映射器规则:Mapper.CreateMap();

这一直有效,直到web api方法变为完全异步.例外情况说它错过了一张地图

 Task<ICollection<Data>> to Task<ICollection<DataItem>>.
Run Code Online (Sandbox Code Playgroud)

映射器已更改为

Mapper.CreateMap<Task<List<Data>>, Task<List<DataItem>>>();
Run Code Online (Sandbox Code Playgroud)

验证配置时,它会抱怨无法映射Result.我们应该如何配置映射?

Jim*_*ard 24

您需要将异步数据提取移出Map调用:

var data = await dataRepository.GetDataAsync();
return Mapper.Map<List<DataItem>>(data);
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用AutoMapper LINQ投影:

var data = await dbContext.Data.ProjectTo<DataItem>().ToListAsync();
Run Code Online (Sandbox Code Playgroud)

我不知道您的存储库是否直接暴露了IQueryable(我们不使用存储库).我们的应用程序现在几乎完全使用第二个版本.


Wil*_*ill 5

何塞的解决方案对我来说非常有用。但是,我确实重新定位了扩展方法以扩展 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;
    }
}
Run Code Online (Sandbox Code Playgroud)