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?
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.
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;
}
}
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;
}
I recently encountered this issue as well. I wanted to keep the async part of the method and therefore, I ended up with a solution that would wrap the await operation inside an async 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);
}
);
}
I would love to hear your thoughts/comments.
Related
I have an async method, say:
public async Task<T> GetAsync()
{
}
and would be called from:
public async Task<IEnumerable<T>> GetAllAsync()
{
foreach (var item in something)
{
var result = await GetAsync();
yield return result;
}
}
The above syntax is not valid but basically I am after asynchronous generators. I know it can be handled via Observables. I did experiment with Rx.NET and it worked to some extent. But I am trying to avoid the complexity it brings to codebase, and more importantly the above requirement is still essentially not a reactive system (ours is still pull based). For e.g. I would only listen to the incoming async streams for a certain time and I have to stop the producer (not just unsubscribe the consumer) from the consumer side.
I can invert the method signature like this:
public IEnumerable<Task<T>> GetAllAsync()
But this makes doing LINQ operations bit tricky without blocking. I want it to be non-blocking as well as without loading the entire thing into memory. This library: AsyncEnumerable does exactly what I am looking for but how can the same be done with Ix.NET? They are meant for the same thing I believe.
In other words, how can I make use of Ix.NET to generate an IAsyncEnumerable when dealing with await? Like,
public async IAsyncEnumerable GetAllAsync()
{
foreach (var item in something)
{
var result = await GetAsync();
return // what?
}
}
(Edited)
Using System.Linq.Async 4.0.0 from NuGet, now you can use SelectAwait.
class Program
{
static void Main(string[] args)
{
Task.Run(async () =>
await GetAllAsync().ForEachAsync((x) => Console.WriteLine(x)));
Thread.Sleep(4000);
}
static IAsyncEnumerable<string> GetAllAsync()
{
var something = new[] { 1, 2, 3 };
return something
.ToAsyncEnumerable()
.SelectAwait(async (x) => await GetAsync(x));
}
static async Task<string> GetAsync(int item)
{
await Task.Delay(1000); // heavy
return "got " + item;
}
}
(Obsolete)
Using System.Interactive.Async 3.2.0 from NuGet, how about this? Currently Select() does not support async lambda, you have to implement it by yourself.
Better support for async - Task based overloads for AsyncEnumerable
class Program
{
static void Main(string[] args)
{
Task.Run(async () =>
await GetAllAsync().ForEachAsync((x) => Console.WriteLine(x)));
Thread.Sleep(4000);
}
static IAsyncEnumerable<string> GetAllAsync()
{
var something = new[] { 1, 2, 3 };
return something.SelectAsync(async (x) => await GetAsync(x));
}
static async Task<string> GetAsync(int item)
{
await Task.Delay(1000); // heavy
return "got " + item;
}
}
static class AsyncEnumerableExtensions
{
public static IAsyncEnumerable<TResult> SelectAsync<T, TResult>(this IEnumerable<T> enumerable, Func<T, Task<TResult>> selector)
{
return AsyncEnumerable.CreateEnumerable(() =>
{
var enumerator = enumerable.GetEnumerator();
var current = default(TResult);
return AsyncEnumerable.CreateEnumerator(async c =>
{
var moveNext = enumerator.MoveNext();
current = moveNext
? await selector(enumerator.Current).ConfigureAwait(false)
: default(TResult);
return moveNext;
},
() => current,
() => enumerator.Dispose());
});
}
}
The extension method is quoted from this sample. https://github.com/maca88/AsyncGenerator/issues/94#issuecomment-385286972
I have a custom async for each implementation that is defined and used as follows:
public static Task ForEachAsync<T>(this IEnumerable<T> source, int partitionCount, Func<T, Task> body)
{
return Task.WhenAll(
from partition in Partitioner.Create(source).GetPartitions(partitionCount)
select Task.Run(async delegate
{
using (partition)
{
while (partition.MoveNext())
{
await body(partition.Current).ConfigureAwait(false);
}
}
})
);
}
...
List<long> ids = new List...
await ids.ForEachAsync(8,
async (id) =>
{
await myTask(id);
}
);
This works great, but now I need to modify this to allow for a cancellation token to be passed in. I have tried something as simple as this:
List<long> ids = new List...
await ids.ForEachAsync(8,
async (id) =>
{
myToken.ThrowIfCancellationRequested();
await myTask(id);
}
);
But this fails ungracefully. Rather than an OperationCanceledException bubbling up, as I would have expected, I am receiving an exception that is being thrown by one of the threads as a result of the cancellation. I have also tried passing the token into the async extension method but that didn't seem to work either. Can someone please provide guidance on how this should be done? Thanks.
To get the exception to bubble up you need to pass the token in to the Task.Run it will just take a small modification to your code.
public static Task ForEachAsync<T>(this IEnumerable<T> source, int partitionCount, Func<T, Task> body, CancellationToken token = default(CancellationToken))
{
return Task.WhenAll(
from partition in Partitioner.Create(source).GetPartitions(partitionCount)
select Task.Run(async delegate
{
using (partition)
{
while (partition.MoveNext())
{
await body(partition.Current).ConfigureAwait(false);
}
}
}, token) //token passed in
);
}
used like
await ids.ForEachAsync(8,
async (id) =>
{
myToken.ThrowIfCancellationRequested();
await myTask(id);
},
myToken //token passed in
);
I have a old data access library that I need to use in my ASP.NET MVC application but I'm having trouble bringing it into the MVC world where many server-side operations are expected to be asynchronous. My data access library looks a like this:
public class MyOldDAL
{
public TaskResult CreateUser(string userName)
{
try
{
// Do user creation
return new TaskResult { Success = true, Message = "success" };
}
catch (Exception ex)
{
return new TaskResult { Success = false, Message = ex.Message };
}
}
}
And is called by my MVC application like this:
public class MyUserStore : IUserStore<ApplicationUser>
{
private readonly MyOldDAL _dal = new MyOldDAL();
public async Task CreateAsync(ApplicationUser user)
{
await Task.Run(() => _dal.CreateUser(user.UserName));
}
}
This is fine until the method in Task.Run() fails for some reason. I'd like to be able to return TaskStatus.Faulted if TaskResult.Success is false but I can't find an example online on how to do it properly - all my Google searches turn up is how to use Task<T> which the IUserStore interface doesn't permit.
For the record, much as I'd like to I can't change MyOldDAL - it's targeting .NET 3.5 so no async or await there!
The normal way to report errors from tasks is via exceptions, so you'll just need to do that transformation yourself:
public class MyUserStore : IUserStore<ApplicationUser>
{
private readonly MyOldDAL _dal = new MyOldDAL();
public Task CreateAsync(ApplicationUser user)
{
var result = _dal.CreateUser(user.UserName);
if (result.Success)
return Task.CompletedTask;
return Task.FromException(new InvalidOperationException(result.Message));
}
}
Note that Task.Run should not be used on ASP.NET.
Note: As Stephen Cleary noticed in his answer, Task.Run should not be used on ASP.NET.
Original answer (before comments):
Your CreateAsync method should normally be like this:
public async Task<TaskResult> CreateAsync(ApplicationUser user)
{
return await Task.Run(() => _dal.CreateUser(user.UserName));
}
But if you can't return Task<TaskResult> from CreateAsync method... well, than you can't obtain TaskResult from CreateAsync by definition. In that case you can store result locally:
private TaskResult taskResult;
public async Task CreateAsync(ApplicationUser user)
{
var result = await Task.Run(() => _dal.CreateUser(user.UserName));
this.taskResult = result;
// process taskResult wherether you need
}
Or raise event with TaskResult payload, allowing client of MyUserStore to subscribe to this event:
public event EventHandler<TaskResult> TaskCompleted;
public async Task CreateAsync(ApplicationUser user)
{
var result = await Task.Run(() => _dal.CreateUser(user.UserName));
this.OnTaskCompleted(result);
}
private void OnTaskCompleted(TaskResult result)
{
this.TaskCompleted?.Invoke(this, result);
}
I would say the following two code snippets I have are equivalent, but they aren't.
The following is working correctly:
var entry3 = Task.Run(async () => await entry2.GetMemberGroupsAsync(false)).WaitForResult().FirstOrDefault();
The following code, where I just moved the Task.Run.WaitForResult chain into an extension method, isn't working, but produces a deadlock:
var entry3 = entry2.GetMemberGroupsAsync(false).RunSynchronouslyAndReturnResult().FirstOrDefault();
public static T RunSynchronouslyAndReturnResult<T>(this Task<T> task)
{
return Task.Run(async () => await task).WaitForResult();
}
Why aren't these two code snippets equivalent?
For completeness's sake, the GetMemberGroupsAsync method is provided by Microsoft Azure Graph API, and the function WaitForResult is defined below. As far as I can see, it doesn't do anything different depending on the caller name or sth. like that:
public static TResult WaitForResult<TResult>(this Task<TResult> task,
bool continueOnCapturedContext = false)
{
if (task == null)
{
throw new ArgumentNullException("task");
}
try
{
return PreventForDeadLocks(task, continueOnCapturedContext).Result;
}
catch (AggregateException ex)
{
if (ex.InnerExceptions.Count == 1)
{
throw ex.InnerExceptions[0];
}
throw;
}
}
public static async Task<TResult> PreventForDeadLocks<TResult>(this Task<TResult> task,
bool continueOnCapturedContext = false)
{
return await task.ConfigureAwait(continueOnCapturedContext: continueOnCapturedContext);
}
The difference here is in which synchronization context your task started. Here:
var entry3 = Task.Run(async () => await entry2.GetMemberGroupsAsync(false)).WaitForResult().FirstOrDefault();
you start your async task (I mean await entry2.GetMemberGroupsAsync(false)) inside Task.Run call, so UI synchronization context is not captured. But here:
var entry3 = entry2.GetMemberGroupsAsync(false).RunSynchronouslyAndReturnResult().FirstOrDefault();
You implicitly start your task (entry2.GetMemberGroupsAsync(false) returns Task) on UI context, so UI synchronization context is captured, and you have your deadlock.
In the first case, GetMemberGroupsAsync is called on a different thread than WaitForResult.
In the second case, it is called on the same thread as WaitForResult. You're just awaiting on a different thread.
I read a few threads about TaskCancellations.. However, I cannot find a solution for a simple question: How do I get a default value when my task fails?
I cannot (!) modify the task itself and put a try catch wrapper around it. I could of course put a try-catch around await, but I would like to handle this with ContinueWith - if possible.
public Task<List<string>> LoadExample()
{
Task<List<string>> task = LoadMyExampleTask();
task.ContinueWith(t => default(List<string>), TaskContinuationOptions.OnlyOnFaulted);
return task;
}
I thought this would be the correct way to deal with the problem. However, my application throws a JsonParseException (which is called in LoadMyExampleTask). I would expect to get null or (even better) an empty list.
In fact, all I want is:
var emptyOrFilledList = await LoadExample(); // guaranteed no exception thrown
Based on Luaan's great answer I wrote an extension method with a defaultValue-option:
public static Task<T> DefaultIfFaulted<T>(this Task<T> #this, T defaultValue = default(T))
{
return #this.ContinueWith(t => t.IsCompleted ? t.Result : defaultValue);
}
Edit: await myTask.DefaultifFaulted() just throwed a
[ERROR] FATAL UNHANDLED EXCEPTION: System.AggregateException
Are you sure that every exception is caught?
If you want that, you must not return the original task - you need to return the continuation.
public Task<List<string>> LoadExample()
{
Task<List<string>> task = LoadMyExampleTask();
return task.ContinueWith(t =>
t.IsFaulted || t.IsCanceled ? default(List<string>) : t.Result);
}
Your original code did allow the continuation to run when the original task faulted, but you didn't read the status of that task - the fact that a task has a continuation which handles errors is entirely irrelevant to what an await on the original task will do.
Of course, it's rather easy to make this into a generic helper method:
public static Task<T> DefaultIfFaulted<T>(this Task<T> #this)
{
return #this.ContinueWith (t => t.IsCanceled || t.IsFaulted ? default(T) : t.Result);
}
As promised, here are the DefaultIfFaulted<T> variants which are true to their name (and the title of this question). They preserve the antecedent task's behavior unless it's faulted (specifically, cancellation is propagated rather than ignored or masked by an AggregateException):
Old-school (.NET 4.0) way:
public static Task<T> DefaultIfFaulted<T>(this Task<T> task)
{
// The continuation simply returns the antecedent task unless it's faulted.
Task<Task<T>> continuation = task.ContinueWith(
t => (t.Status == TaskStatus.Faulted) ? Task.FromResult(default(T)) : t,
TaskContinuationOptions.ExecuteSynchronously
);
return continuation.Unwrap();
}
Async/await way (simple but slower):
public static async Task<T> DefaultIfFaulted<T>(this Task<T> task)
{
try
{
return await task.ConfigureAwait(false);
}
catch (Exception ex) when (!(ex is OperationCanceledException))
{
return default(T);
}
}
Async/await way (perf almost identical to Unwrap):
public static async Task<T> DefaultIfFaulted<T>(this Task<T> task)
{
// Await completion regardless of resulting Status (alternatively you can use try/catch).
await task
.ContinueWith(_ => { }, TaskContinuationOptions.ExecuteSynchronously)
.ConfigureAwait(false);
return task.Status != TaskStatus.Faulted
// This await preserves the task's behaviour
// in all cases other than faulted.
? await task.ConfigureAwait(continueOnCapturedContext: false)
: default(T);
}
Tests (passed by all of the above):
using Xunit;
[Fact]
public async Task DefaultIfFaultedTest()
{
var success = Task.Run(() => 42);
var faulted = Task.Run(new Func<int>(() => { throw new InvalidOperationException(); }));
Assert.Equal(42, await success.DefaultIfFaulted());
Assert.Equal(0, await faulted.DefaultIfFaulted());
await Assert.ThrowsAsync<TaskCanceledException>(() =>
{
var tcs = new TaskCompletionSource<int>();
tcs.SetCanceled();
return tcs.Task.DefaultIfFaulted();
});
}