Any reason to use async/await when the controller action is sync? - c#

Lets say I have a controller action that cannot be made async (for various reasons), but I have a service that (through several methods) calls a rest service using HttpClient. Is there any thing to gain by using the async client and using .Wait or .Result? Or would it be less performant to use the synchronous method?
So either:
//MyController.cs
public ActionResult GetSomething(int id)
{
//lots of stuff here
var processedResponse = _myService.Get(id);
//lots more stuff here
return new ContentResult(result);
}
//MyService.cs
public ProcessedResponse Get(int id)
{
var client = new HttpClient();
var result = client.Get(_url+id);
return Process(result);
}
Or:
//MyController.cs
public ActionResult GetSomething(int id)
{
//lots of stuff here
var processedResponse = _myService.GetAsync(id).Result;
//or, .Wait, or Task.Run(...), or something similar
//lots more stuff here
return new ContentResult(result);
}
//MyService.cs
public async Task<ProcessedResponse> GetAsync(int id)
{
var client = new HttpClient();
var result = await client.GetAsync(_url+id);
return await Process(result);
}

Is there any thing to gain by using the async client and wrapping the
method in Task.Run(() => _myService.Get()).Result?
The only thing you'll most likely end up gaining is a deadlock. Think about it, you're queuing a naturally asynchronous method on a thread-pool thread, where the ASP.NET already gave you a thread to process you Action inside. There isn't much sense in that.
If you want to go async, and think you'll actually benefit from the scale provided by asynchrony, then you should re-factor your controllers to be async as well and return a Task<T>, where you can await those asynchronous methods.
So I'd either stay synchronous, or re-factor the code top to bottom to support async:
//MyController.cs
public async Task<ActionResult> GetSomethingAsync(int id)
{
//lots of stuff here
await GetAsync(id);
return new ContentResult(result);
}
//MyService.cs
public async Task<ProcessedResponse> GetAsync(int id)
{
var client = new HttpClient();
var result = await client.GetAsync(_url+id);
return await Process(result);
}

In your scenario, no there isn't a good reason, but lets add some functionality:
//MyController.cs
public ActionResult GetSomething(int id)
{
//lots of stuff here
var processedResponse = _myService.GetAsync(id).Result;
//or, .Wait, or Task.Run(...), or something similar
//lots more stuff here
return new ContentResult(result);
}
//MyService.cs
public async Task<ProcessedResponse> GetAsync(int id)
{
var client = new HttpClient();
var pendingResult1 = client.GetAsync(_url+id);
var pendingResult2 = someAsyncOperation();
var result3 = someSyncOperation();
var result1 = await pendingResult;
var result2 = await pendingResult2;
return await Process(result1, result2, result3);
}
Now, since your request takes a while to complete, someAsynchOperation starts executing immediately instead of waiting for GetAsync() to complete. someSyncOperation is also being executed in the meantime.
Without the async keyword, you would not be able to use await, so it's better to have it if you plan to have asynchronous execution inside your funciton.

It gets interesting when do can do it like this
//MyController.cs
public ActionResult GetSomething(int id)
{
var processedResponseTask = _myService.GetAsyn(id)
//lots of stuff here (1)
var processedResponseTask.Wait();
var processedResponse = processedResponseTask.Result;
//lots more stuff here (2)
return new ContentResult(result);
}
now the lots of stuff here(1) is done in parallel with your async task. (Or if you called your service twice for example). If you don't actually do a lot around lots of stuff here(1) then there isn't much of a point.

Related

What happens when we don't await an async method

I have a .NET CORE 2 backend. In one of my controller endpoints, I'm creating invitations to be sent out via email. This seems to be a huge bottleneck on the endpoint and after thinking about it, I don't really need to wait for these invitations. If the email fails to send out, I can't really do anything about it anyway.
If I don't do await sendFn() would it essentially be a fire and forget method? I was reading on another stackoverflow thread that I'd have to do sendFn().ContinueWith(t => throw(t)) to be able to catch the exception since it'll be in another thread.
I have similar mailing functions around the code base. They each do slightly different things, but is there a service fn I can do to wrap these to make them fire and forget? I think some places I can just not use await (if that works), but some things alter the database context so if I don't await them I can potentially run into a case where something is accessing the same db context.
[HttpPost]
public async Task<IActionResult> CreateEvent([FromBody] Event val)
{
_ctx.Event.Add(val);
await _ctx.SaveChangesAsync();
await SendInvitations(val); // fn in question
return Ok();
}
public async Task SendInvitation(Event event)
{
forEach (var person in event.people)
{
await _ctx.Invitation.Add(person); // This shouldn't happen while another iteration or some other async code elsewhere is using the db ctx.
_ctx.SaveChangesAsync();
await _mailService.SendMail(person.email,"you have been invited"); // don't really need to await this.
}
}
I'm posting to my server with data about an event. After I create and save the event to the database, I go and create invitations for each person. These invitations are also database items. I then send out an email. I'm mostly worried that if I drop the await, then when I'm creating invitations, it may conflict with db context elsewhere or the next iteration.
To get your code to compile and run I had to make these changes:
public async Task<IActionResult> CreateEvent(Event val)
{
_ctx.Event.Add(val);
await _ctx.SaveChangesAsync();
await SendInvitation(val);
return Ok();
}
public async Task SendInvitation(Event #event)
{
foreach (var person in #event.people)
{
await _ctx.Invitation.Add(person);
await _ctx.SaveChangesAsync();
await _mailService.SendMail(person.email, "you have been invited");
}
}
I also had to write this harness code:
public OK Ok() => new OK();
public class Event
{
public List<Person> people = new List<Person>();
}
public class Person
{
public string email;
}
public interface IActionResult { }
public class OK : IActionResult { }
public class Invitation
{
public Task Add(Person person) => Task.Run(() => { });
}
public static class _ctx
{
public static List<Event> Event = new List<Event>();
public static Invitation Invitation = new Invitation();
public static Task SaveChangesAsync() { return Task.Run(() => { }); }
}
public static class _mailService
{
public static Task SendMail(string email, string message) { return Task.Run(() => { }); }
}
Then I updated SendInvitation like this:
public async Task SendInvitation(Event #event)
{
Thread.Sleep(2000);
foreach (var person in #event.people)
{
await _ctx.Invitation.Add(person);
await _ctx.SaveChangesAsync();
await _mailService.SendMail(person.email, "you have been invited");
}
Console.WriteLine("Done `SendInvitation`.");
}
Now, I can run it all like so:
var e = new Event();
e.people.Add(new Person() { email = "foo#bar.com" });
CreateEvent(e).ContinueWith(t => Console.WriteLine("Done `CreateEvent`."));
Console.WriteLine("Done `Main`.");
That outputs:
Done `Main`.
Then 2 seconds later:
Done `SendInvitation`.
Done `CreateEvent`.
If I simply change CreateEvent to this:
public async Task<IActionResult> CreateEvent(Event val)
{
_ctx.Event.Add(val);
await _ctx.SaveChangesAsync();
Task.Run(() => SendInvitation(val));
return Ok();
}
Then I get this output:
Done `Main`.
Done `CreateEvent`.
Then 2 seconds later:
Done `SendInvitation`.
That seems to be what you want.
The short answer is that you have no guarantees that that the execution of that code will complete.
That's why ASP.NET Core has infrastructure for background work: Implementing background tasks in .NET Core 2.x webapps or microservices with IHostedService and the BackgroundService class

Async for Long operation with HttpResponseMessage

I am figuring out performance of my C# web api. I wrote a very simple HelloWorld response:
public class HelloWorldController : ApiController
{
public HttpResponseMessage Get()
{
return new HttpResponseMessage()
{
Content = new StringContent("HelloWorld")
};
}
}
I tested by using JMeter I set 1000 Users request and it worked perfectly (CPU usage up to 100%). But the problem is that when the operation of the api take bit longer, the response become worse, got only 3 responses each (CPU usage <7%). It took minuets for 1000 users request.
public HttpResponseMessage Get()
{
Thread.Sleep(1000);
return new HttpResponseMessage()
{
Content = new StringContent("HelloWorld")
};
}
After google I come up with idea of using async but still I got same problem. I don't know what the problem is or my code implementation. Below is my sample implementation.
public async Task<HttpResponseMessage> Get()
{
return new HttpResponseMessage()
{
Content = new StringContent(await LongOperationAsync())
};
}
private string LongOperation()
{
//long operation here
Thread.Sleep(1000);
return "HelloWorld";
}
private Task<string> LongOperationAsync()
{
return Task.Factory.StartNew(() => LongOperation());
}
Anyone know what is the problem or any idea regarding to this problem?
async and await are not magic bullets that just "mak teh codez moah awesomz". On ASP.NET, await enables your application to be more scalable (and respond to changes in scale more quickly) by making optimum use of the thread pool.
So, if you're freeing up a thread pool thread (await) but using up another thread pool thread (StartNew), you're not going to gain anything. In particular, exposing a fake-async method for a synchronous API is an antipattern.
If possible, the best solution is to make LongOperationAsync a naturally-asynchronous operation:
public async Task<HttpResponseMessage> Get()
{
return new HttpResponseMessage()
{
Content = new StringContent(await LongOperationAsync())
};
}
private async Task<string> LongOperationAsync()
{
//long operation here
await Task.Delay(1000);
return "HelloWorld";
}
If this isn't possible, then you may as well keep it synchronous. Using Task.Run (or even worse, StartNew) isn't going to help at all.
The Method LongOperationAsync And LongOperation also should be async :
private async Task<string> LongOperation()
{
//long operation here
await Task.Delay(1000);
return "HelloWorld";
}
private async Task<string> LongOperationAsync()
{
var rt = await Task.Run(() => LongOperation());
return rt;
}
see:
Asynchronous programming

Task.Wait always returns false although task finished

I'm using HttpClient trying to execute a POST method in Web API controller. The controller method is synchronous. I'm doing so this way:
var response = owin.HttpClient.PostAsJsonAsync(uri, body);
After that I'm calling Wait:
var result = response.Wait(15000);
When running this code, I see the http finish executing, yet the result value is always false. What can I be missing?
Edit:
I now tried an async approach yet it didn't help me as well
public IHttpActionResult Add(Item item)
{
var result = _db.AddItem(item);
return Ok(result);
}
Test project:
TestServer _owinTestServer;
public async Task<HttpResponse message> Method1(string url, object body)
{
return await
_owinTestServer.HttpClient.PostAsJsonAsync(url,body);
}
public async Task<ItemPreview> Method2(object body);
{
return await Method1("..", body ).Result.Content.ReadAsAsync<ItemPreview>();
}
[TestMethod]
public void test1()
{
Item item = new(...);
Method2(item).Continue with(task => {// Never reach here }
}
What am I doing wrong?
When debugging I see that the controller method returns a good response, yet it never reaches back to my test
You are mixing async and blocking calls (ie .Result, .Wait()) which are leading to a deadlock.
This looks more like it is a blocking issue on the test client side.
You need to make the test async all the way through in this case if you want to await on results from the server.
Convert test method to async
[TestMethod]
public async Task test1() {
//Arrange
Item item = new Item(...);
//Act
var preview = await Method2(item);
//Assert
Assert.IsNotNull(preview);
}
And update the methods to not mix async and blocking calls.
Method1 does not need asyn/await if it is not using the task after the call so it can be removed and just have the method return the Task that can be awaited
TestServer _owinTestServer;
public Task<HttpResponse> Method1(string url, object body) {
return _owinTestServer.HttpClient.PostAsJsonAsync(url, body);
}
Method2 needs to await the response from Method1 and then get its content.
public async Task<ItemPreview> Method2(object body) {
var response = await Method1("..", body );
return await response.Content.ReadAsAsync<ItemPreview>();
}
It doesn't matter if the controller method is synchronous or not. That is purely a concern on the server code. Since the PostAsJsonAsync method is asynchronous, you need to await it:
var response = await owin.HttpClient.PostAsJsonAsync(uri, body);
Which will allow your code to wait for the response from the server.
I assume your initial code looked something like this...
var response = owin.HttpClient.PostAsJsonAsync(uri, body);
var result = response.Wait(15000);
var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
...
And yes, result will be false after waiting the 15 seconds. This is because you have setup the request, A.K.A response, but you haven't actually made the called and any response.Wait(n) will return false.
You just need to start the ReadAsAsync...
var response = owin.HttpClient.PostAsJsonAsync(uri, body);
var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
However, I think you will find you can skip the response.Wait(n) all together, as it will return true because the ReadAsAsync() will wait to return or fail. If you're looking to configure the request timeout, you can do that in the HttpClient and your ReadAsAsync will throw an AggregateException with an InnerException of TaskCanceledException.
On a side note, you don't need to use owin.HttpClient you can just instantiate a new HttpClient. I believe the owin object you are referring to is for self hosting your WebApi, I don't know if that matters. But let's say you are calling Add(Item item) on your WebApi and that db.AddItem(item) will return and ItemPreview object, your code could look like this:
[TestMethod]
public void test1()
{
Item item = new(...);
var uri = "..";
var client = new HttpClient();
var response = client.PostAsJsonAsync(uri, item);
var itemPreview = response.Result.Content.ReadAsAsync<ItemPreview>();
/* The things to happen once you have item preview */
}
The result may always be false because the _db.AddItem is returning false all the time. If not, I've made a change in your code ideally which should work for you
TestServer _owinTestServer;
public async Task<HttpResponse message> Method1(string url, object body)
{
return await _owinTestServer.HttpClient.PostAsJsonAsync(url,body);
}
public async Task<ItemPreview> Method2(object body);
{
return await Method1("..", body ).Result.Content.ReadAsAsync<ItemPreview>();
}
[TestMethod]
public void test1()
{
Item item = new(...);
await Method2(item).ContinueWith(task => {// Never reach here }
}
Since Method2 returns an Async Task, the ContinueWith will not wait for it to complete and hence it may required await on the method invocation.

Call async await method in sync calling method

This is only the idea on what im doing in a window service.
I get the idea from this video to do it in parallel processing.
I have two different method and a model class.
Model Class code:
public class Email(){
public string Recipient { get; set; }
public string Message { get; set; }
}
Methods is something like this:
public void LoadData(){
while(Main.IsProcessRunning){
// 1. Get All Emails
var emails = new dummyRepositories().GetAllEmails(); //This will return List<Emails>.
// 2. Send it
// After sending assume that the data will move to other table so it will not be query again for the next loop.
SendDataParallel(emails);//this will function async? even though the calling method is sync.
// This will continue here or wait until it already send?
// If it will continue here even though it will not send already
// So there's a chance to get the email again for the next loop and send it again?
}
}
//This will send email at parallel
public async void SendDataParallel(IList<Email> emails){
var allTasks = emails.Select(SendDataAsync);
await TaskEx.WhenAll(allTasks);
}
//Assume this code will send email asynchronously. (this will not send email, for sample only)
public async void SendDataAsync(Email email){
using (var client = new HttpClient())
{
client.PostAsync(email);
}
}
I only want to get all queued emails then send it in parallel then wait until it already send.
I'm avoiding using foreach on every email that I get.
Lets start at the bottom:
You dispose your client before you actually finish receiving the HttpResponseMessage asynchronously. You'll need to make your method async Task and await inside:
public async Task SendDataAsync(Email email)
{
using (var client = new HttpClient())
{
var response = await client.PostAsync(email);
}
}
Currently, your SendDataParallel doesn't compile. Again, it needs to return a Task:
public Task SendEmailsAsync(IList<Email> emails)
{
var emailTasks = emails.Select(SendDataAsync);
return Task.WhenAll(allTasks);
}
At the top, you'll need to await on SendEmailsAsync:
public async Task LoadDataAsync()
{
while (Main.IsProcessRunning)
{
var emails = new dummyRepositories().GetAllEmails();
await SendEmailsAsync(emails);
}
}
Edit:
If you're running this inside a windows service, you can offload it to Task.Run and use the async keyword:
var controller = new Controller();
_processThread = Task.Run(async () => await controller.LoadDataAsync());
Doesn't your compiler highlight your code with errors?
If you mark your method as async while it doesn't return any value, you should set your return type as Task, not void:
public async Task SendDataParallel(IList<Email> emails){
var allTasks = emails.Select(SendDataAsync);
await Task.WhenAll(allTasks);
}
Your second method also shoud return a Task, otherwise what you want to (a)wait in the first method?
public async Task SendDataAsync(Email email){
using (var client = new HttpClient())
{
return client.PostAsync(email);
}
}
Now you can Select all your SendDataAsync tasks in SendDataParallel and .Wait() it's task in LoadData in synchronious mode:
public void LoadData(){
while(Main.IsProcessRunning){
var emails = new dummyRepositories().GetAllEmails(); //This will return List<Emails>.
SendDataParallel(emails).Wait();
}
}
More information you can find reading answers in other SO questions and docs on MSDN:
Can somebody please explain async / await?
Brief explanation of Async/Await in .Net 4.5
how to and when use async and await
Asynchronous Programming with Async and Await
And as you used LINQ's Select() which is based on foreach cycle next article also could be useful:
Nested task inside loop

Converting convenience methods that use Tasks

I often write code that has convenience methods which basically wrap other methods. Here's a simple example:
public class WithoutAsync
{
public static ReadOnlyCollection<Response> GetResponses(IEnumerable<Request> fromRequests)
{
var ret = new List<Response>();
foreach (Request r in fromRequests)
{
ret.Add(new Response());
}
return ret.AsReadOnly();
}
//convenience method
public static Response GetResponse(Request fromRequest)
{
return GetResponses(new Request[] {fromRequest})[0];
}
}
Now I want to await long-running operations but I can't quite figure out how to retrofit this methodology for use with TPL:
public class WithAsync
{
public static async Task<ReadOnlyCollection<Response>> GetResponses(IEnumerable<Request> fromRequests)
{
var awaitableResponses = new List<Task<Response>>();
foreach (Request r in fromRequests)
{
awaitableResponses.Add(Task.Run<Response>(async () =>
{
await Task.Delay(10000); //simulate some long running async op.
return new Response();
}));
}
return new List<Response>(await Task.WhenAll(awaitableResponses)).AsReadOnly();
}
//convenience method
public static Task<Response> GetResponse(Request fromRequest)
{
return GetResponse(new Request[] { fromRequest });
}
}
The convenience method above obviously won't work because it's trying to return a Task<ReadOnlyCollection<Response>> when it really needs to return a Task<Response>.
This works:
//convenience method
public static Task<Response> GetResponse(Request fromRequest)
{
return new Task<Response>(new Func<Response>(() => GetResponse(new Request[] { fromRequest }).Result[0]));
}
but it seems really awkward, and more importantly, it blocks on .Result[0] which is potentially on a UI thread.
Is there any good way to accomplish what I'm trying to do?
You're trying to avoid making that "convenience method" async, but there's no reason to do that.
What you want is to call the other method, wait until there are responses and then get the first and only one. You can do that by making it async and using await:
async Task<Response> GetResponseAsync(Request fromRequest)
{
var responses = await GetResponsesAsync(new[] { fromRequest });
return responses.Single();
}
Although a better solution in this specific case is to switch things around and have the single GetResponse actually do that work of a single request, and have the multiple GetRsponses call it instead:
async Task<ReadOnlyCollection<Response>> GetResponsesAsync(IEnumerable<Request> fromRequests)
{
return (await Task.WhenAll(fromRequests.Select(GetResponse))).ToList().AsReadOnly();
}
async Task<Response> GetResponseAsync(Request fromRequest)
{
await Task.Delay(10000); //simulate some long running async op.
return new Response();
}
Notes:
I know it's an example, but there's probably no reason to use Task.Run instead of a simple async call.
The convention is to name async methods with an "Async" suffix (i.e. GetResponseAsync).
I've also pluralized the name of the method that returns a collection.
I'm still sticking with I3arnon's answer because it's a well-written, informative answer, but I'd like to submit my own answer because I realized that I was almost there. Here's the async convenience method I was struggling to find:
//convenience method
public static async Task<Response> GetResponse(Request fromRequest)
{
return (await GetResponses(new Request[] { fromRequest }))[0];
}

Categories

Resources