Should async or non-async methods be used in SignalR Hub? - c#

I have the following Hub class in SignalR part and here I defined all of the methods related to connection:
public override Task OnConnected()
{
// here I cannot call this, and need to convert this method async
await AddToGroup("stockGroup");
//
string name = Context.User.Identity.Name;
_connections.Add(name, Context.ConnectionId);
return base.OnConnected();
}
public async Task AddToGroup(string groupName)
{
await Groups.Add(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} joined");
}
I have a look at many different examples regarding to these connection methods (and other Hub methods) and see that some of them use async method while some of them not. In this example above, I need to convert OnConnected() method to async in order to call AddToGroup() method. Of course the reverse situation would also be ok, but I am not sure which one is better. So, should I use async methods or non-async methods for all of the methods in the Hub? Any help would be appreciated.
Update 1 : Converted method (to async).
public override async Task OnConnected()
{
await AddToGroup("stockGroup");
string name = Context.User.Identity.Name;
_connections.Add(name, Context.ConnectionId);
return base.OnConnected();
}
Update 2:
public override async Task OnConnected()
{
// #1 There is no async method in "Microsoft.AspNet.SignalR" library.
//await Groups.AddToGroupAsync(Context.ConnectionId, "SignalR Users");
// #2 I just have sync version of "AddToGroupAsync()" and used it
await Groups.Add(Context.ConnectionId, "SignalR Users");
/* #3 I think there is no need to use this custom method in the Hub.
Because the same method is already exist in the IGroupManager interface */
//await AddToGroup("jiraGroup");
string name = Context.User.Identity.Name;
_connections.Add(name, Context.ConnectionId);
// #4 Here also the same problem and I used sync version of OnConnected()
//await base.OnConnectedAsync();
await base.OnConnected();
}

Just add async if you need await inside method. async is a marker for compiler to generate async state machine.
should I use async- use if you need to await.
If you don't need to await just return task itself without async. In that case you can avoid creation of state machine.
Also please read Async/Await FAQ from Stephen Toub:
When you mark a method with the “async” keyword, you’re really telling
the compiler two things:
You’re telling the compiler that you want to be able to use the
“await” keyword inside the method (you can use the await keyword if
and only if the method or lambda it’s in is marked as async). In doing
so, you’re telling the compiler to compile the method using a state
machine, such that the method will be able to suspend and then resume
asynchronously at await points.
You’re telling the compiler to “lift”
the result of the method or any exceptions that may occur into the
return type. For a method that returns Task or Task, this
means that any returned value or exception that goes unhandled within
the method is stored into the result task. For a method that returns
void, this means that any exceptions are propagated to the caller’s
context via whatever “SynchronizationContext” was current at the time
of the method’s initial invocation.

You should use Async methods with await and return Task.
Asynchrony is viral so you should avoid async void.
Bad:
public async void MethodAsync()
{
var result = await SendAsync();
DoSomething(result);
}
Good:
public async Task MethodAsync()
{
var result = await SendAsync();
DoSomething(result);
}
There are some awesome async guidelines by #davidfowl here.
Update: remove the return:
public override async Task OnConnected()
{
await AddToGroup("stockGroup");
string name = Context.User.Identity.Name;
_connections.Add(name, Context.ConnectionId);
await base.OnConnected();
}

Related

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

Convert conventional method to Asynchronous

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.

Unable to await SetStringAsync in IDistributedCache

I am using .NET Core 2.0 with Microsoft.Extensions.Caching.Redis 2.0. Why is IDistributedCache.SetStringAsync not actually marked as async, and therefore not awaitable?
var services = new ServiceCollection();
services.AddDistributedRedisCache(options =>
{
options.Configuration = "127.0.0.1";
options.InstanceName = "master";
});
var provider = services.BuildServiceProvider();
var cache = provider.GetService<IDistributedCache>();
// Does not compile
await cache.SetStringAsync("mykey", "myvalue");
Those methods are defined to return Task (they are extension methods in DistributedCacheExtensions):
public static Task SetStringAsync(this IDistributedCache cache, string key, string value)
{
return cache.SetStringAsync(key, value, new DistributedCacheEntryOptions());
}
And you can use await with such kind of method as (MSDN)
The task to which the await operator is applied typically is returned by a call to a method that implements the Task-Based Asynchronous Pattern. They include methods that return Task, Task<TResult>, and System.Threading.Tasks.ValueType<TResult> objects.
but you cannot use await in the method that is not marked as async, that's why you have a compilation error:
await can only be used in an asynchronous method modified by the async keyword.
If you are interested in what would have been the difference if the team had marked SetStringAsync as async method, like:
public static async Task SetStringAsync
then look into SO "async Task then await Task" vs "Task then return task"

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

Get the current Task instance in an async method body

If I have an async method body like so -
public async Task GetSomething() {
await SendText("hi");
await SendImage("bla.bmp");
}
How can I get the Task object before it is returned to the user when the await kicks in
ie..
public async Task GetSomething() {
myTasks.Add(Task.Current);
await SendText("hi");
await SendImage("bla.bmp");
//some processing
}
so that somewhere else I can do
await Task.WhenAll(myTasks);
Console.WriteLine("All tasks complete");
This is so that I can wait for all tasks to complete before shutting down
This is not directly possible as the language does not have a facility to access the "current" task.
There is a workaround though: Wrap your async method in another method. This other method can get hold of the task once the async method returns (which happens approximately at the first await point).
In all cases I recommend letting the caller add the async Task to your list, not the async method itself. This is useful even from an encapsulation point of view.

Categories

Resources