【问题标题】:Map async result with automapper使用自动映射器映射异步结果
【发布时间】:2015-12-13 02:23:24
【问题描述】:

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

第一步是获取数据:

    public List<DataItem>> GetData()
    {
        return Mapper.Map<List<DataItem>>(dataRepository.GetData());
    }

这就像一个魅力。然后我们将数据仓库设为异步,并更改为使用它的代码。

    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 感到困惑。首先,我们有以下映射器规则: 映射器.CreateMap();

这一直有效,直到 web api 方法变得完全异步。异常说它缺少来自

的地图
 Task<ICollection<Data>> to Task<ICollection<DataItem>>.

映射器已更改为

Mapper.CreateMap<Task<List<Data>>, Task<List<DataItem>>>();

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

【问题讨论】:

  • 你能改成:var data = await dataRepository.GetDataAsync(); return Mapper.Map&lt;List&lt;DataItem&gt;&gt;(data);吗?类似于这里的:stackoverflow.com/questions/24827417/…
  • 但是控制器本身不再异步了?
  • 该方法将被标记为异步并在正文中包含等待,因此您不会丢失映射并且仍然是异步的

标签: c# async-await automapper


【解决方案1】:

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

var data = await dataRepository.GetDataAsync();
return Mapper.Map<List<DataItem>>(data);

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

var data = await dbContext.Data.ProjectTo<DataItem>().ToListAsync();

我不知道您的存储库是否直接公开 IQueryable(我们不使用存储库)。如今,我们的应用几乎只使用第二个版本。

【讨论】:

  • 我们正在使用存储库来包装 MongoDB。存储库返回一个列表,需要映射到我们的视图模型(以限制推送到前端的数据)。据我了解,MongoDB 总是返回文档,所以如果你进行投影,它是在应用程序中完成的。
  • @jimmy-bogard,这个呢? ;) _licenceRepository.Include(l =&gt; l.LicenceType).SingleOrDefaultAsync(ConditionLambda).ContinueWith(source =&gt; _mapper.Map&lt;LicenceDto&gt;(source.Result))
  • 我尝试了你的第一种方法,它对我有用,谢谢!
  • 只是需要考虑的事情;将 .Result 添加到任何异步调用的末尾
【解决方案2】:

事实证明,Jose 的解决方案对我非常有用。但是,我确实将扩展方法重新定位为扩展 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】:

    您还可以添加几个任务扩展方法来为您执行此操作:

        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;
        }
    

    【讨论】:

      【解决方案4】:

      我最近也遇到了这个问题。我想保留方法的异步部分,因此,我最终得到了一个解决方案,它将等待操作包装在异步任务中。

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

      我很想听听你的想法/cmets。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-12-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-21
        • 2021-10-28
        • 1970-01-01
        相关资源
        最近更新 更多