Convert conventional method to Asynchronous - c#

I have an API which is responsible for inserting text message details in database.
It does by making synchronous call to repository which I think could be implemented asynchronous way.How can I achieve this? Or what could be the best way to handle this scenario.Code snippet example is highly appreciated as I am still getting my ahead wrapping around .NET.
api:
public IHttpActionResult SendSMSNotification([FromBody] SMSNotification smsNotification)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
_service.SendSMS(smsNotification);
return Ok();
}
Service:
internal void SendSMS(SMSNotification smsNotification)
{
_repository.Notify(_mapperService.GetSMSNotification(smsNotification));
}
mapper:
public SMSNotification GetSMSNotification(SMSNotification message)
{
return AutoMapper.Mapper.Map<SMSNotification>(message);
}
repo:
public virtual bool Notify(SMSNotification request)
{
using (var sql = _sqlMapper.CreateCommand('Database', 'Stored proc'))
{
sql.AddParam("#fMessage", request.Message);
//..............
//.............. more params
var retvalParamOutput = sql.AddOutputParam("#fRetVal", System.Data.SqlDbType.Int);
sql.Execute();
return retvalParamOutput.GetSafeValue<int>() == 1;
}
}
sql here is a custom thing and it has following methods:
public static int Execute(this IDataCommand #this);
[AsyncStateMachine(typeof(<ExecuteAsync>d__1))]
public static Task<int> ExecuteAsync(this IDataCommand #this);

Changing a blocking, typically IO-bound call (such as database, network or file system work) to async can make your app scale better.
This does have a flow-on affect through your API. That is, you need to be awaiting on asynchronous calls all the way up to the top-most call, otherwise, somewhere is going to block and you're just lost the benefit of calling an async API.
To demonstrate that, let's start from the bottom at the repository call, as that's the possibly expensive blocking operation can be made async. We alter sql.Execute to use the asynchronous version ExecutAsync version instead:
repo:
public virtual async Task<bool> Notify(SMSNotification request)
{
using (var sql = _sqlMapper.CreateCommand('Database', 'Stored proc'))
{
sql.AddParam("#fMessage", request.Message);
//..............
//.............. more params
var retvalParamOutput = sql.AddOutputParam("#fRetVal", System.Data.SqlDbType.Int);
await sql.ExecuteAsync();
return retvalParamOutput.GetSafeValue<int>() == 1;
}
}
Now here we have to change the signature of the method to return a Task wrapping a result of bool.
We also mark the method as async, so then we can use the "await" operator further down. Without doing this, we'd have to do more refactoring to manipulate and return the Task result ourselves, but the "async" modifier and "await" keyword let the compiler do that magic for us, and the rest of our code mostly looks like normal.
The mapper call doesn't really need to change:
mapper:
public SMSNotification GetSMSNotification(SMSNotification message)
{
return AutoMapper.Mapper.Map<SMSNotification>(message);
}
The service call is now making a call to an async method, so because we want to await and not block on that async call, we have to also change this previously void method to an async method. Note we change it from "void" to "async Task"; you CAN mark a void method as "async void", but that's intended as a workaround for event handlers in Windows Forms and WPF apps; in every other case you want to change a "void" method to "async Task" when making it async.
Service:
internal async Task SendSMS(SMSNotification smsNotification)
{
await _repository.Notify(_mapperService.GetSMSNotification(smsNotification));
}
Then finally, our API call can be made async, and await our service call:
api:
public async Task<IHttpActionResult> SendSMSNotification([FromBody] SMSNotification smsNotification)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
await _service.SendSMS(smsNotification);
return Ok();
}
It's sometimes recommended that after you do a refactor like this, to rename the methods to end in "Async" by convention; however I don't think this is really compulsory, as so much of the .NET API surface is becoming async, it's almost redundant now.
It's worth getting your head around the async / await stuff though; I've tried to keep the example relatively brief. But I hope this at least gets you started.

Related

How call async method from non async web API

I am having web api, which is non async.
From that web api, i am calling method
SmtpClient.SendMailAsync(email);
This email get sent to respective person, but the next web api request get fails.
public class TestController : ApiController
{
public TestController(TestService testService)
{
_testService = testService;
}
public IHttpActionResult Post(data)
{
_testService.SendEmail(data);
}
}
public class TestService
{
public async Task SendEmail(MailMessage email)
{
SmtpClient client = new SmtpClient();
client.SendMailAsync(email)
}
}
From the days before the async/await pattern was introduced, there are still many non-asychronous functions around in Framework classes.
SmtpClient client is one of the classes old enough for this. The SendFunction are the droids you are look for:
https://learn.microsoft.com/en-us/dotnet/api/system.net.mail.smtpclient.send
While the naming is a bit off and they return void, those seems to be the pre-async functions. Failure should be communicated via Exceptions in both cases.
Given your comment that you want to have SendEmail behave like fire & forget, I would propose using
Task.Run(() => _testService.SendEmail(data));
This will give the unit of work to the threadpool and free your request from the duty of waiting for this task. Generelly this is advised for fire & forget.
As a rule of thumb otherwise, it's generally a bad idea to call asynchronous things from a synchronous context. Do async all the way, or be prepared for deadlocking. For example, you could simply make your controller actions asynchronous as well.
If you will need use some result of sending:
public async IHttpActionResult Post(data)
{
var t = Task.Run(() =>_testService.SendEmail(data));
var result = await t;
// do something with result
}
- but of course your SendEmail function should return Task<> instead of Task ...

How can I call an async method within a sync method?

I'm trying to call an async task (SIn) within a synch method (SignIn). I need the synch method because I'm passing ref to that method. But when I'm calling the async task, the GUI is frozen. The async task is a simple login with the onedrive sdk.
I've tried to waited the task, but the GUI still frozen. I've also tried creating a new Thread, but it didn't work too. How can I call the async method?
public override bool SignIn(ref User user)
{
try
{
signInEnd = false;
signinUser = user;
Task<bool> task = SIn();
task.Wait();
return task.Result;
}
catch(Exception e)
{
return false;
}
}
public async Task<bool> SIn()
{
var msaAuthProvider = new MsaAuthenticationProvider(
this.oneDriveClientId,
this.oneDriveReturnUrl,
this.scopes,
new CredentialVault(this.oneDriveClientId));
await msaAuthProvider.AuthenticateUserAsync();
driveClient = new OneDriveClient(this.oneDriveBaseUrl, msaAuthProvider);
}
Calling Wait() blocks the UI thread which means that the continuation of SIn(), i.e. the part that will eventually be executed once the Task returned by AuthenticateUserAsync() has completed, won't be able to execute on this thread. This results in a deadlock.
You may be able to get around this by avoiding capturing the context by calling ConfigureAwait(false) in SIn():
public async Task<bool> SIn()
{
var msaAuthProvider = new MsaAuthenticationProvider(
this.oneDriveClientId,
this.oneDriveReturnUrl,
this.scopes,
new CredentialVault(this.oneDriveClientId));
await msaAuthProvider.AuthenticateUserAsync().ConfigureAwait(false);
driveClient = new OneDriveClient(this.oneDriveBaseUrl, msaAuthProvider);
}
But the "real" solution to this kind of issues is not to mix asynchronous and synchronous code, i.e. SignIn should be asynchronous and await SIn(). Don't block on asynchronous code by calling Wait() or Result:
public Task<bool> SignIn(User user)
{
try
{
...
return await SIn();
}
catch (Exception e)
{
return false;
}
}
Please refer to #Stephen Cleary's blog post for more information about this.
mm8 is right that not calling async from inside a sync method is the best way to solve your issue,
remember that the public async void EventHandler() method was specifically designed for running long running tasks from a gui linked control
However it isn't always possible to rewrite an entire system to be async when only one small section needs changing
In this case you should avoid waiting for the results as this makes the async process pointless, what you can do though is break your synchronous code into 2 parts a before and after
the before method will prep and launch the task,
the after handles the results
ie
public async Task<string> GetData(int delay)
{
await Task.Delay(delay);
return "complete";
}
public void StartGettingData()
{
GetData(5000).ContinueWith(t => CompleteGetData(t.Result), TaskScheduler.Current);
}
public void CompleteGetData(string message)
{
UpdateStatus(message);
}
this method does have the added complexity of requiring you to ensure thread safety yourself, which is why the async/await functionality was introduced

Asyn process stopped when object is disposed in API

While i call the web api, asyn method start process. After completed API process then asyn process also stopped. Here is my sample code snippet. Please give any solution for this.
private readonly ITestService testservice;
public TestController(ITestService test)
{
this.testservice=test;
}
[Route("Sample/SaveMyData")]
[HttpPost]
public IHttpActionResult SaveMyData(MyInfo info)
{
MyInfo inf = new MyInfo ();
inf = testservice.SaveMyInformation(info);
SendMailProcess(inf)
return Ok<MyInfo>(inf);
}
private async Task<bool> SendMailProcess(MyInfo emailInfo)
{
await Task.Run(()=> this.testservice.SendMail(emailInfo));
return true;
}
#Eraiarasu, you are creating a separate Thread in your SendMailProcess and thats why your API call completes before the SendMailProcess completes.
Also, If you are trying to create async methods then please try to put await on SendMailProcess method invocation in SaveMyData as well. Also make SaveMyData as async too. Note that, you have SendAsync method in smtp api in .NET. So you don't have to use Task.Run as you did in SendMailProcess

Async method that does not make async calls

I need to create a method that will connect to my Azure SQL database and read some data. All the methods that call this method are async methods but I don't think I can make the ReadStateProvinceListAsync an async one.
What's the right approach in these situations? Just ignore the "warning" from the compiler that I have an async method that does not use the await keyword or make the ReadStateProvinceListAsync method a regular synchronous method in which case the GetStateProvinceListAsync method will give me the same warning.
I want to do it right -- by the book. That's why I want to learn the right approach.
public static async Task<List<StateProvince>> GetStateProvinceListAsync(string countryId)
{
// Check to see if I already have this data cached
if(gotData)
{
// Life is good! Get data from cache.
}
else
{
// Don't have the data cached. Call the DB read method
statesList = await ReadStateProvinceListAsync(countryId)
}
}
private static async Task<List<StateProvince>> ReadStateProvinceListAsync(string countryId)
{
// Call Azure SQL Database to read data. No async code here!
}
There's absolutely no value in marking a synchronous method with the async keyword. If your method doesn't make any asynchronous calls make it synchronous:
public static List<StateProvince> GetStateProvinceListAsync(string countryId)
{
// Check to see if I already have this data cached
if(gotData)
{
// Life is good! Get data from cache.
}
else
{
// Don't have the data cached. Call the DB read method
statesList = ReadStateProvinceList(countryId)
}
}
private static List<StateProvince> ReadStateProvinceList(string countryId)
{
// Call Azure SQL Database to read data. No async code here!
}
If for some reason you must return a task (e.g. it's an interface or abstract implementation) use Task.FromResult to return a Task synchronously:
private static Task<List<StateProvince>> ReadStateProvinceListAsync(string countryId)
{
return Task.FromResult(ReadStateProvinceList());
}

Correct way of calling await inside SignalR 2.0 OnReceived handler

I'm using SignalR 2.0.1 with PersistentConnection (not hubs) and currently my default very simple OnReceived handler looks like this:
protected override Task OnReceived(IRequest request, string connectionId, string data)
{
return Connection.Broadcast(data);
}
I want to add some I/O-bound code to this handler, like accessing a DB on a different machine. Naturally I want everything to be asynchronous and I don't want to block the thread so I'm hoping to use an async flavor like db.SaveChangesAsync() of EF6 (instead of the regular blocking db.SaveChanges).
I added the db.SaveChangesAsync() to the handler, but I need to await on it too. So I've also added an async modifier to the handler, but this caused an error with my return value - I can't return Connection.Broadcast(data) anymore.
This is what I got eventually:
protected override async Task OnReceived(IRequest request, string connectionId, string data)
{
// some EF code here..
await db.SaveChangesAsync();
// the original return changes into this?
await Connection.Broadcast(data);
}
Is this the correct way to do this? Because I have a feeling I'm abusing the pattern.
btw, if I understand correctly, the current version of SignalR is fully asynchronous. Older versions like the one described here had two handlers - one synchronous and one asynchronous (with the Async postfix).
Yes, that is a perfectly reasonable way of going about it. Why do you feel that you may be abusing the pattern?
Think of it this way:
a synchronous method returning void corresponds to an async method returning Task. Likewise,
a synchronous method returning T corresponds to an async method returning Task<T>.
That is why you cannot do
protected override async Task OnReceived(IRequest request, string connectionId, string data)
{
return Connection.Broadcast(data);
}
, since the async keyword and your return statement would indicate a method that returned Task<Task>.
What you can do is remove your last await altogether. All it will do is create an empty continuation (because it is essentially saying "when the broadcast is done, run the code after the broadcast and until the ending curly brace"). Or you can leave it in for consistency, if you prefer.
I have an async intro on my blog that you may find helpful.
A Task instance represents a "future". So when you're doing this:
protected override Task OnReceived(IRequest request, string connectionId, string data)
{
return Connection.Broadcast(data);
}
You're saying "OnReceived is done when Connection.Broadcast(data) is done". This is practically the same as:
protected override async Task OnReceived(IRequest request, string connectionId, string data)
{
await Connection.Broadcast(data);
}
Which is saying "OnReceived will (asynchronously) wait for Connection.Broadcast(data) to be done, and then OnReceived will be done." It's slightly more efficient without the async and await, but they have practically the same semantics.
So, yes, this code would be correct:
protected override async Task OnReceived(IRequest request, string connectionId, string data)
{
// some EF code here..
await db.SaveChangesAsync();
await Connection.Broadcast(data);
}

Categories

Resources