Why does this sync wrapper over an async method work? - c#

Given:
A legacy non-async API method on an ASP.NET/WCF web service
New async internal library
New async Web API controller that should be used going forward
A "storage provider" object that only has an async interface. Its tests pass when run asynchronously, and when run synchronously outside a request context.
The option "go async all the way" is not on the table, since it would break backward compatibility.
public class Impl {
// This works fine when used asynchronously
public Task<Guid> SaveThingAsync(Thing thingToSave) {
return await _storageProvider.saveAsync(thingToSave);
}
public Guid SaveThing(Thing thingToSave) {
// "Obviously", this code creates a deadlock when called
// from within the request context
// return SaveThingAsync(thingToSave).Result
// Not so obviously, this also creates a deadlock
// return SaveThingAsync(thingToSave)
// .ConfigureAwait(false)
// .GetAwaiter()
// .GetResult()
// This was deadlocking, but magically stopped
// return Task.Run(
// async () => await SaveThingAsync(thingToSave)
// .ConfigureAwait(false)
// ).Result;
// This one works
var saveTask = Task.Run(async () =>
await SaveThingAsync(thingToSave)));
var result = saveTask.ConfigureAwait(false).GetAwaiter().GetResult();
return result;
}
Why?

Task.Run steps "outside" the request context - it just runs on the thread pool context. So, it won't deadlock because SaveThingAsync doesn't resume on the request context.
On a side note, the ConfigureAwait(false) is meaningless there, since there is no await to configure.
On another side note, "async all the way" should still be an option. WebAPI and WCF clients don't care whether the implementation they're calling is synchronous or asynchronous. Changing a WCF method implemented synchronously to a WCF method implemented asynchronously is invisible to client code.

Related

ASP.NET Web API - difference between Async and Synchronous method in browser's Network Console

I'm trying to understand this code that I just found in this link:
I'm curious of what's the difference when between using the asynchronous and synchronous method in the Web API:
Asynchronous:
[HttpGet("{id}", Name = "GetBook")]
public async Task<IActionResult> GetBookWithBookCovers(Guid id)
{
var bookEntity = await _booksRepository.GetBookAsync(id);
if (bookEntity == null)
{
return NotFound();
}
// get bookcovers
var bookCovers = await _booksRepository.GetBookCoversAsync(id);
// map book & covers into one BookWithCovers
var mappedBook = _mapper.Map<BookWithCovers>(bookEntity);
return Ok(_mapper.Map(bookCovers, mappedBook));
}
Synchronous:
[HttpGet("{id}", Name = "GetBook")]
public IActionResult GetBookWithBookCovers(Guid id)
{
var bookEntity = _booksRepository.GetBook(id);
if (bookEntity == null)
{
return NotFound();
}
// get bookcovers
var bookCovers = _booksRepository.GetBookCovers(id);
// map book & covers into one BookWithCovers
var mappedBook = _mapper.Map<BookWithCovers>(bookEntity);
return Ok(_mapper.Map(bookCovers, mappedBook));
}
If there is a long running query in one of the codes of these two methods, what will be the behavior in the network console of browser?
Is the asynchronous method going to return a status code 202 while the query is running? or it will error out and saying like query timeout?
As Ian Kemp said "async/await ... has no effect on the HTTP protocol".
Client perspective
There is not difference. The same OK status code would return in both cases if no exception would occur during the request processing.
Sync or Async processing should be considered as an implementation detail. If you would have a OpenAPI documentation (a.k.a Swagger) then the two methods would look exactly the same.
Server perspective
Your ASP.NET WebAPI would not return anything to the caller until it reaches the end of the Controller's Action.
The await keyword says:
There is possibly a long-running operation.
The executing Thread can't move on to the next statement, because it relies on the result of the async operation.
Because the Thread can't do anything with this request, it would make sense to return to the ThreadPool and assign a new job to it (for example another request) until the async operation is running.
When that async operation is finished the ThreadPool will be notified and it will schedule the remaining of the Controller's Action to the appropriate Thread.
The async/await was designed (primarily) to support non-blocking async I/O operations. That means while the network driver handles the I/O request until that time the computation executors (Threads) could work on other things.
So in short, async/await gives you scalability (bigger throughput) for server applications.

best practice for using async await in webapi

I have .NET core Web API which as service layer. Service layer has all EF code.
If have basecontroller with this code
protected Task<IActionResult> NewTask(Func<IActionResult> callback)
{
return Task.Factory.StartNew(() =>
{
try
{
return callback();
}
catch (Exception ex)
{
Logger.LogError(ex.ToString());
throw;
}
});
}
In controller action I wrap all calls to service in above method e.g. :
[HttpGet("something")]
public async Task<IActionResult> GetSomething(int somethingId)
{
return await NewTask(() =>
{
var result = _somethingService.GetSomething(somethingId);
if (result != null)
return Ok(result);
else
return NotFound("Role not found");
});
}
Is this correct pattern considering tomorrow I may have more than one service calls in action or making calls to other webservice. Please advise.
i want my api to benefit from async await thing.does above pattern will serve these needs
No, it does not. Running synchronous work on the thread pool gives you the drawbacks of synchronous and asynchronous code, with the benefits of neither.
something service has some crud operations which use entityframework core
Currently, your action method is what I call "fake asynchronous" - it looks asynchronous (e.g., using await), but in fact is just running blocking code on a background thread. On ASP.NET, you want true asynchrony, whicn means you must be async all the way. For more about why this is bad on ASP.NET, see the first half of my intro to async on ASP.NET article (it mostly deals with ASP.NET non-core, but the first part talking about synchronous vs asynchronous requests is valid for any kind of server).
To make this truly asynchronous, you should start at the lowest level - in this case, your EFCore calls. They all support asynchrony. So, replace API calls like x.FirstOrDefault() with await x.FirstOrDefaultAsync() (and the same for all your creates/updates/deletes, etc).
Then allow async/await to grow naturally from there; the compiler will guide you. You'll end up with asynchronous methods on your somethingService which can be consumed as such:
[HttpGet("something")]
public async Task<IActionResult> GetSomething(int somethingId)
{
var result = await _somethingService.GetSomethingAsync(somethingId);
if (result != null)
return Ok(result);
else
return NotFound("Role not found");
}
Okay, first of all, you should stop using Task.Factory.StartNew and use Task.Run only when you have heavy CPU-bound work that you want to run on a thread pool thread. In you case you don't really need that at all. Also you should remember that you should only use Task.Run when calling a method and not in the implementation of the method. You can read more about that here.
What you really want in your case is to have asynchronous work inside your service (I'm not really sure you even need a service in your case) when you are actually making a call to the database and you want to use async/await and not just run some stuff on a background thread.
Basically your service should look something like this (if you are sure you need a service):
class PeopleService
{
public async Task<Person> GetPersonByIdAsync(int id)
{
Person randomPerson = await DataContext.People.FirstOrDefaultAsync(x => x.Id == id);
return randomPerson;
}
}
As you can see your service now makes async calls to the database and that's basically what your pattern should be. You can apply this to all your operations(add/delete/ etc..)
After making your service asynchronous you should be easily able to consume the data in the action.
Your actions should look something like this:
[HttpGet("something")]
public async Task<IActionResult> GetPerson(int id)
{
var result = await PeopleService.GetPersonByIdAsync(id);
if (result != null)
return Ok(result);
else
return NotFound("Role not found");
}

Async does not work in asynchronous controller mvc 4.0

I have MVC 4.0 application targated at targetFramework="4.5".
I have to basically convert the existing functionality of file processing from synchronous to asynchronous (so that for large file user don't have to wait for other task).
My code is
[HttpPost]
public async Task<ActionResult> FileUpload(HttpPostedFileBase fileUpload)
{
Coreservice objVDS = new Coreservice ();
//validate the contents of the file
model =objVDS. ValidateFileContents(fileUpload);
// if file is valid start processing asynchronously
await Task.Factory.StartNew(() => { objVDS.ProcessValidFile(model); }, CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.FromCurrentSynchronizationContext());
return view();
}
Basically I want to call a asynchronous method which is in services which does database operations( diffrent project).
I want asynchronous process to have access to the context in services methods. Thats why I am using
TaskScheduler.FromCurrentSynchronizationContext() in Task.Factory.StartNew().
The service method is like following in which, based on file type, a second service is getting called for data operations
public async task ProcessValidFile(fileProcessDataModel model)
{
employeeWorkedDataservice service =new employeeWorkedDataservice()
await Task.Factory.StartNew(() =>
{
service .ProcessEmployeeDataFile(model.DataSetToProcess, OriginalFileName, this, model.Source);
},
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.FromCurrentSynchronizationContext());
}
ProcessEmployeeDataFile returns void and its not asynchronous method.
When the code above is executed it does not return to controller untill it completes the data processing. I think that I am missing something here.
Please guide me to solution.
Thanks,
Amol
Looks like you've misunderstood how await works.
Read this https://msdn.microsoft.com/en-us/library/hh191443.aspx#BKMK_WhatHappensUnderstandinganAsyncMethod
Setting something running in a task will allow it to run asynchronously so you can do something else while it's running.
When you need the result to continue, you use the await keyword.
By creating your task an immediately awaiting it, you're instantly blocking until the task resolves; making it effectively synchronous.
If you're happy to return to your view without waiting for processing to complete, I don't think you need await at all, since at no point do you want to wait for the result of the operation.
public task ProcessValidFile(fileProcessDataModel model)
{
employeeWorkedDataservice service =new employeeWorkedDataservice()
return Task.Factory.StartNew(() =>
{
service.ProcessEmployeeDataFile(model.DataSetToProcess, OriginalFileName, this, model.Source);
},
CancellationToken.None,
TaskCreationOptions.DenyChildAttach,
TaskScheduler.FromCurrentSynchronizationContext());
}
[HttpPost]
public ActionResult FileUpload(HttpPostedFileBase fileUpload)
{
Coreservice objVDS = new Coreservice ();
//validate the contents of the file
model =objVDS. ValidateFileContents(fileUpload);
// if file is valid start processing asynchronously
// This returns a task, but if we're not interested in waiting
// for its results, we can ignore it.
objVDS.ProcessValidFile(model);
return view();
}
Regarding your comments:
I would seriously consider not passing your controller to your service, or having your service rely on the session and context since you're tightly coupling your business logic to your API controller.
Get the bits you need from the controller while you're in it and pass them to your service.
I have to basically convert the existing functionality of file processing from synchronous to asynchronous (so that for large file user don't have to wait for other task).
That's not what async does; as I describe on my blog, async does not change the HTTP protocol.
What you want is some form of "fire and forget" on ASP.NET. I have another blog post that covers a few solutions. Note that using Task.Factory.StartNew is the most dangerous of all these solutions.
The best (read: most reliable) solution is to use a proper distributed architecture: your ASP.NET app should create a description of the work to be done and place that in a reliable queue (e.g., MSMQ); then have an independent backend (e.g., Win32 service) that processes the queue. This is complex, but much less error-prone than attempting to force ASP.NET to do something it was never meant to do.

EntLib TransientFaultHandling RetryPolicy.ExecuteAsync synchronization context

I'm using the Enterprise Library Transient Fault Handling Application Block in a Windows 8 Store App with the WCF Data Services Client for ODATA. I want to use retry logic for transient errors occurring when calling the ODATA service. I built a custom transient error detection strategy.
I have also built a LoadTaskAsync extension method for the DataServiceCollection as the LoadAsync method does not return a Task (instead the DataServiceCollection raises the LoadCompleted event).
So I can load data into a DataServiceCollection as follows:
var query = this.DataContext.Products.Where(item => item.Modified >= anchor);
var products = new DataServiceCollection<Product>(this.DataContext);
await this.retryPolicy.ExecuteAsync(() => products.LoadTaskAsync(query));
Now the documentation for the Enterprise Library Transient Fault Handling Application Block states that
The taskFunc argument you pass to the ExecuteAsync method is not necessarily invoked in the same synchronization context used when calling ExecuteAsync originally; so if you need to start the task from within the UI thread for example, be sure to schedule it explicitly within the delegate.
I need to invoke the LoadTaskAsync method on the UI thread as the load operation may update products that were already tracked by the data context and that are data bound to the UI.
Question is how? Preferably without modifying the LoadTaskAsync extension method (what if it was not my code to change). I was thinking of creating an extension method for the RetryPolicy that calls the ExecuteAsync method while making sure the taskFunc is invoked on the UI thread.
The easiest way perhaps is to modify the LoadTaskAsync extension method to pass in a TaskCreationOptions.AttachedToParent, so I could create an extension method for the RetryPolicy as follows:
public static Task<TResult> ExecuteCurrentSynchronizationContextAsync<TResult>(
this RetryPolicy retryPolicy,
Func<TaskCreationOptions, Task<TResult>> taskFunc)
{
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
return
retryPolicy.ExecuteAsync(
() =>
Task.Factory.StartNew(
() => taskFunc(TaskCreationOptions.AttachedToParent),
CancellationToken.None,
TaskCreationOptions.None,
scheduler).Unwrap());
}
Note that taskFunc must now be a Func<TaskCreationOptions, Task<TResult>>.
I would then call this as follows:
await
this.retryPolicy.ExecuteCurrentSynchronizationContextAsync(
creationOptions => products.LoadTaskAsync(query, creationOptions));
As I prefer not to change the LoadTaskAsync extension method, how could I change this RetryPolicy ExecuteCurrentSynchronizationContextAsync extension method so that taskFunc can be a Func<Task<TResult>> again while making sure taskFunc is invoked on the UI thread?
I would not recommend using AttachedToParent with asynchronous tasks. In fact, most promise-style tasks will specify DenyChildAttach which prevents AttachedToParent from working.
Instead, you just need to capture the synchronization context itself and use that. There is a wrinkle: Windows Store apps don't allow synchronous invocation on the synchronization context, so you'll need to either use CoreDispatcher.RunAsync instead of SynchronizationContext, or build your own async-aware extension method for SynchronizationContext. Of those two, I prefer using the SynchronizationContext approach; it's a bit more code in this case, but it means you don't have to tie your code (presumably service-layer code) to this specific UI framework.
So, first we define a RunAsync on SynchronizationContext, which will (asynchronously) execute asynchronous code on a specified synchronization context:
public static Task<TResult> RunAsync<TResult>(this SynchronizationContext context, Func<Task<TResult>> func)
{
var tcs = new TaskCompletionSource<TResult>();
context.Post(async _ =>
{
try
{
tcs.TrySetResult(await func());
}
catch (OperationCanceledException)
{
tcs.TrySetCanceled();
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}, null);
return tcs.Task;
}
Then we can capture and use the SynchronizationContext:
public static Task<TResult> ExecuteOnCurrentSynchronizationContextAsync<TResult>(
this RetryPolicy retryPolicy,
Func<Task<TResult>> taskFunc)
{
var context = SynchronizationContext.Current ?? new SynchronizationContext();
return retryPolicy.ExecuteAsync(() => context.RunAsync(taskFunc));
}

Using HttpContext in Async Task

I have the following mvc action.
public async Task<JsonResult> DoSomeLongRunningOperation()
{
return await Task.Run(() =>
{
//Do a lot of long running stuff
//The underlying framework uses the HttpContext.Current.User.Identity.Name so the user is passed on the messagebus.
}
}
In the task the HttpContext gets null. We did a lot of tricking, but nothing assures us of the HttpContext being available always in our new thread.
Is there a solution to use HttpContext within out async tasks?
In our IocContainer we have registered the following object which passes the username to the framework.
public class HttpContextUserIdentityName : ICredentials
{
public string Name
{
get { return HttpContext.Current.User.Identity.Name; }
}
}
This code is called in a lot of places before persisting to the database.
We need either another way of getting the username of the user initiated the webrequest or fix the issue with the HttpContext being null.
Because the persisting to the database happens in the Task I can't access the HttpContext before entering the task.
I also can't think of a safe way to temporary persist the username so I can implement another ICredentials service object.
You almost never want to use Task.Run in an ASP.NET method.
I think the cleanest solution (but the most work) is to implement async-compatible interfaces at your other layers:
public async Task<JsonResult> DoSomeLongRunningOperation()
{
//Do a lot of long running stuff
var intermediateResult = await DoLongRunningStuff();
return await DetermineFinalResult(intermediateResult);
}
You should get whatever information you need from the current context before you start the new thread. In this case, add something like:
string username = HttpContext.Current.User.Username;
before Task.Run and then use that inside of the other thread.
On a side note, as it stands, there's no reason to await the task. You can just return the task directly and not mark the method as Async.
If you need to access the Response object, which will presumably to utilize the results of the long running operation and thus can't be before Task.Run you should do so after the Task.Run (but ensure that the task is awaited). If you end up doing this then you can't do what I suggested in my previous paragraph.
I would try passing in the reference to the HttpContext as the state object, because that should create a new instance of that object on the stack for the thread that executes the work. Instead of using Task.Run, use
return await Task.Factory.StartNew((ctx) =>
{
var context = (HttpContext)ctx;
//Do stuff
}, httpContextObject);
Task.Run and Task.Factory.StartNew return immediately, so asp.net continues on in the event lifecycle in the worker thread that is handling the request while your thread is operating on the object that has already been disposed.

Categories