创建用于映射的任务扩展方法

时间:2019-03-18 08:18:23

标签: c# asynchronous async-await

我想要的

我们使用 AutoMapper ,我们的整个服务层都是异步。这是我们当前在API操作中的映射外观:

var response = (await this.service.GetAll())
    .AsQueryable()
    .ProjectTo<DataViewModel>();
...

我想实现以下目标:

var response = await this.service
    .GetAll()
    .Map<DataViewModel>();

我做了什么

到目前为止,我取得的最好成绩是:

var response = await this.service
    .GetAll()
    .Map<DataServiceModel, DataViewModel>();

扩展方法如下:

// Task extension
public static async Task<IEnumerable<TDestination>> MapCollection<TSource, TDestination>(
    this Task<IEnumerable<TSource>> collection)
        => (await collection)
            .MapCollection<TDestination>();

// IEnumerable extension
public static IEnumerable<TDestination> MapCollection<TDestination>(
    this IEnumerable collection)
{
    foreach (var item in collection)
    {
        yield return Mapper.Map<TDestination>(item);
    }
}

这是可以接受的,但是理想情况下,我想在任务扩展中删除TSource的依赖关系,因为映射逻辑不需要它。

问题

如果我删除TSource,将无法使编译器同意我的意见。当我尝试使用如下定义的扩展名时:

public static async Task<IEnumerable<TDestination>> MapCollection<TDestination>(
    this Task<IEnumerable> collection)
        => (await collection)
            .MapCollection<TDestination>();

编译器大喊:

  

'Task<IEnumerable<DataServiceModel>>'不包含   'MapCollection'和最佳扩展方法重载   'TaskExtensions.MapCollection<DataViewModel>(Task<IEnumerable>)'   需要类型为“ Task<IEnumerable>”的接收者

因此,如果没有显式的泛型TSource,我将无法识别它。我不明白问题出在哪里,因为编译器同意我的IEnumerable扩展名,该扩展名定义为

MapCollection<TDestination>(this IEnumerable collection)

但不允许:

MapCollection<TDestination>(this Task<IEnumerable> collection)

问题

  1. 我能以某种方式解决上述问题,并使用真正需要的唯一通用参数来定义我的Task扩展名吗?
  2. 这种Task的用法是否存在性能问题?即线程块,死锁等。我不这么认为,但是我仍然缺乏对asnyc编程的深入了解。

1 个答案:

答案 0 :(得分:1)

您最大的问题是部分类型推断,并且无法解决

但是,如果您不介意打个电话,可以使用扩展方法包装器类

public class Wrapper<TSource>
{
   private readonly Task<IEnumerable<TSource>> _sourceCollection;

   public Wrapper(Task<IEnumerable<TSource>> source) 
      =>_sourceCollection = source;

   public async Task<IEnumerable<TDest>> To<TDest>()
      => (IEnumerable<TDest>)Mapper.Map(await _sourceCollection, _sourceCollection.GetType(), typeof(IEnumerable<TDest>));

}

public static class Extensions
{
   public static Wrapper<TSource> Map<TSource>(this Task<IEnumerable<TSource>> source)
      => new Wrapper<TSource>(source);
}

用法

await service.GetAllAsync()
             .Map()
             .To<Something>();

注释1 :这是完全未经测试的,即使它确实起作用也缺少基本的健全性检查 < / p>

注释2 :这对于额外的异步层来说还是有点浪费的