使用自动映射器映射异步结果
Map async result with automapper
我们正在创建 angularjs 应用程序的 Web.Api 应用程序。 Web.Api returns 一个 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 感到困惑。首先,我们有以下映射器规则:
Mapper.CreateMap();
这一直有效,直到网络 api 方法变得完全异步。异常表示它缺少来自
的地图
Task<ICollection<Data>> to Task<ICollection<DataItem>>.
映射器已更改为
Mapper.CreateMap<Task<List<Data>>, Task<List<DataItem>>>();
验证配置时,它抱怨无法映射结果。我们应该如何配置映射?
您需要将异步数据提取移出地图调用:
var data = await dataRepository.GetDataAsync();
return Mapper.Map<List<DataItem>>(data);
或者,您可以使用 AutoMapper LINQ 投影:
var data = await dbContext.Data.ProjectTo<DataItem>().ToListAsync();
我不知道您的存储库是否直接公开了 IQueryable(我们不使用存储库)。这些天我们的应用程序几乎只使用第二个版本。
您还可以添加一些任务扩展方法来为您执行此操作:
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;
}
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;
}
}
我最近也遇到了这个问题。我想保留该方法的异步部分,因此,我最终得到了一个将等待操作包装在异步任务中的解决方案。
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);
}
);
}
我很想听听你的 thoughts/comments。
我们正在创建 angularjs 应用程序的 Web.Api 应用程序。 Web.Api returns 一个 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 感到困惑。首先,我们有以下映射器规则: Mapper.CreateMap();
这一直有效,直到网络 api 方法变得完全异步。异常表示它缺少来自
的地图 Task<ICollection<Data>> to Task<ICollection<DataItem>>.
映射器已更改为
Mapper.CreateMap<Task<List<Data>>, Task<List<DataItem>>>();
验证配置时,它抱怨无法映射结果。我们应该如何配置映射?
您需要将异步数据提取移出地图调用:
var data = await dataRepository.GetDataAsync();
return Mapper.Map<List<DataItem>>(data);
或者,您可以使用 AutoMapper LINQ 投影:
var data = await dbContext.Data.ProjectTo<DataItem>().ToListAsync();
我不知道您的存储库是否直接公开了 IQueryable(我们不使用存储库)。这些天我们的应用程序几乎只使用第二个版本。
您还可以添加一些任务扩展方法来为您执行此操作:
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;
}
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;
}
}
我最近也遇到了这个问题。我想保留该方法的异步部分,因此,我最终得到了一个将等待操作包装在异步任务中的解决方案。
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);
}
);
}
我很想听听你的 thoughts/comments。