Map async result with automapper

故事扮演 提交于 2019-12-03 12:36:17

问题


We are createing a Web.Api application of a angularjs application. The Web.Api returns a json result.

Step one was getting the data:

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

That worked like a charm. Then we made the data repo async and we changed to code to work with it.

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

Stil no problems. Now we wanted to make my Web.Api full async awayt so we changed it to:

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

At this moment Automapper gets confused. First we had the following mapper rule: Mapper.CreateMap();

This worked until the web api method became full async. The exception said it was missing a map from

 Task<ICollection<Data>> to Task<ICollection<DataItem>>.

The mapper was changed to

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

When validating the configuration it complains that Result cannot be mapped. How should we configure the mappings?


回答1:


You need to move the async data fetch out of the Map call:

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

Alternatively, you can use AutoMapper LINQ projections:

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

I don't know if your repository exposes IQueryable directly (we don't use repositories). Our apps use the second version pretty much exclusively these days.




回答2:


You can also add a couple of Task Extension Methods that will do this for you:

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



回答3:


Jose's solution proved quite useful to me. I did, however, re-target the extension method to extend IMapper which allowed me to remove the singleton Mapper reference.

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


来源:https://stackoverflow.com/questions/32609607/map-async-result-with-automapper

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!