SignalR in .NET 5.0 not detecting abrupt disconnections - signalr

I am running a SignalR Hub in .NET 5.0 in a Docker container. I am using the inbuild SignalR package for .NET 5.0.
It is failing to detect abrupt disconnections. If I instruct SignalR to drop the connection from client-side, it detects the disconnection, but if I just close the browser SignalR doesn't detect the disconnection.
This is the functional equivalent of the code I am using to detect disconnections.
public override async Task OnDisconnectedAsync(Exception exception)
{
var userId = Map.GetUserFromMapping(Context.ConnectionId
Map.Unmap(userId, Context.ConnectionId);
await base.OnDisconnectedAsync(exception);
}
I know this problem existed in AspNetCore 2.1 and earlier versions, but I understood that this problem was supposedly fixed in later versions.
Why are the abrupt disconnection not being detected? What factors could/would be affecting this?

Solved my own problem. My setup was falling back on Long-Polling which doesn't have keep alive functionality and therefore doesn't detect disconnections.

Related

Is it possible to use SignalR on Azure to communicate between .Net Core and MVC5?

I have an AzureSignalR instance created and 2 client apps. One is an MVC5, .Net 4.7.2 app and the other a .Net Core 2.2. I've create an identical SignalR Hub in both that is simply
public class MessageHub : Hub
{
public void Send(ApplicationEvent evt)
{
// broadcastMessage must be a javascript method on the client
Clients.All.broadcastMessage(evt);
}
}
and implemented the necessary javascript on both. The problem I have is that while messages can be sent and received by each of the apps separately, they do not receive messages raised by the other.
I can see a slight difference in the request to the AzureSignalR service in that the values in the asrs.op parameter are different i.e.
MVC5
https://<redacted>.service.signalr.net/aspnetclient/negotiate?clientProtocol=2.1&_=1572008814078&asrs_request_id=1tgsLXwGAAA%3D&asrs.op=%2Fsignalr&connectionData=%5B%7B%22name%22%3A%22messagehub%22%7D%5D
.Net Core
https://<redacted>.service.signalr.net/client/negotiate?hub=messagehub&asrs.op=%2Fmessagehub&asrs_request_id=NmhXZHcGAAA%3D
and wondered if this was the reason, but I can't see a way to change the MVC5 op name as it is inferred.
Obviously we'll be upgrading the MVC5 app at some stage, so this would be ideal in the interim.
You are using two different types of SignalR here. One is the new one for .NET Core an other is old SignalR for .NET Framework applications. You can see the differences between both on this microsoft documentation.
Since those are two different implementations of SignalR, it is the reason why they don't communicate with each other. Azure SignalR is more for helping you scale out your SignalR instances and don't bother with sticky sessions, Redis backplane configurations and other infrastructural stuff.

ASP.Net Core Identity canceled when using FindByIdAsync

I'm have a small project that uses the Asp.Net Core Identity framework together with EF Core.
One function calls the UserManager.FindByIdAsync(id) and it returns the proper object. However, it only works a few minutes after the application is started. As long as the server is busy it works fine, but as soon as the application is idle more than 1-2 minutes the request fails.
It fails with:
*OperationCanceledException: The operation was canceled.
System.Threading.CancellationToken.ThrowOperationCanceledException()*
The stacktrace looks like this:
*System.Threading.CancellationToken.ThrowOperationCanceledException()
Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore.FindByIdAsync(string userId, CancellationToken cancellationToken)
Microsoft.AspNetCore.Identity.UserManager.FindByIdAsync(string userId)
MyProject.Areas.Admin.ControllerServices.UserService+<GetUser>d__11.MoveNext() in UserService.cs*
I'm still logged in as other pages are working fine.
A simple call to the EF context.Users.FindAsync(new object[] { id }) will work as expected, but the next line containing FindByIdAsync will fail.
All this works perfect in the dev environment, the error occurs when the application is installed on the server running IIS on WS 2008 R2. Recycling the app pool will make it work again until it is idle again for a few minutes.
I have noted that when lines like 'Connection id "0HL5E91K33IIQ" reset.' are being logged, then the app starts to fail. Prior to that it works.
FindByIdAsync is not the only identity function to fail, many other functions fails with the same error.
What am I missing?
I will answer my own question, and hopefully this will help someone else in the future.
For me, it all boiled down to the lifetime of the injected services.
UserManager depends on IHttpContextAccessor (this is where the CancellationToken comes from) and it behaves incorrectly when lifetimes do not match up.
IHttpContextAccessor is added as a Singleton service, while the UserManager is added as a scoped service. My service that used the UserManager was added as a Singleton service.
Changing this to Scoped made the errors go away.
In my case it boiled down to the exact same issue, but was caused by a very subtle design "flaw" in ASP.Net Core's DI implementation. For various reasons I prefer using SimpleInjector, but stuffing ASP.Net Identity Core into it is hard, compared to the nice extension methods provided for the build in container. So I put the framework stuff in the framework container, and my business stuff in SimpleInjector, deciding that "Authentication and Authorization" is considered "framework". Only the AccountController is resolved by the framework container using cross wiring. However, using app.ApplicationServices.GetService<AccountController>() outside a request scope does not fail but returns a Singleton that will survive! Unfortunately exactly this happens when you let SimpleInjector verify it's configuration. The first request causing a malfunction (bad login) leaves your whole runtime with a defect singleton instance. Solution for this is well documented in SimpleInjectors documentation, use their extension app.GetRequiredRequestService<AccountController>() instead.
Update asp.net core 2.0
Now, you won't get the fishy singleton instance, but an exception:
System.InvalidOperationException: 'Cannot resolve scoped service 'WebApplication15.Controllers.AccountController' from root provider.'

SignalR in multiple instances in Azure

I'd like to use Azure to host my web application, for instance CloudService web role or Azure Websites, inside the application I use SignalR to connect client and server.
Since I scaled two instances for my web roles, it seems I came across a very common problem, the SignalR could not find the correct original instance. The client JavaScript said it was already started, but the server hub OnConnected event randomly not raised, so were the server methods which intended to be called by clients, all these strange issues happened randomly.
Once I changed the instance to be one, all the problems gone. So can anyone explain what happened when the client call server method, why sometimes the server seems not response properly?
I found the post, can Azure Service Bus solve this issue?
Yes, you need to use the azure service bus. Otherwise the connections are stored in memory on the given server and the other server will know nothing about them. Once you create the service bus, just reference it in the startup class.
public void Configuration(IAppBuilder app)
{
System.Diagnostics.Trace.TraceInformation("SignalR Startup > Configurtion start");
// Any connection or hub wire up and configuration should go here
string connectionString = "XXX";
GlobalHost.DependencyResolver.UseServiceBus(connectionString, "TopicName");
...
}
You will also need to get a reference to the context in each of your hub methods:
var context = GlobalHost.ConnectionManager.GetHubContext<HubName>();
It's easy peasy :)

SignalR- Removing a Client From signalR Hub

Is it possible to remove a client from signalR hub using its connectionId? I'am trying to create an application in which I can remove a client from hub?
SignalR does not provide an API to disconnect clients. You could workaround it by defining a client method and in the body call connection.stop()
SignalR version 2 does not have a built-in server API for disconnecting clients. In the current SignalR release, the simplest way to disconnect a client from the server is to implement a disconnect method on the client and call that method from the server. The following code sample shows a disconnect method for a JavaScript client using the generated proxy.
var myHubProxy = $.connection.myHub
myHubProxy.client.stopClient = function() {
$.connection.hub.stop();
};
Security Note: Neither this method for disconnecting clients nor the proposed built-in API will address the scenario of hacked clients that are running malicious code, since the clients could reconnect or the hacked code might remove the stopClient method or change what it does. The appropriate place to implement stateful denial-of-service (DOS) protection is not in the framework or the server layer, but rather in front-end infrastructure.
More details here

SignalR1.0.1 - Maintain connectionId

I'm using SignalR 1.0.1 and I want to maintain connection id even on page refresh.
Yes, I know, this is a common question of signalr so I got good answered post also but in all of them they mentioned "IConnectionIdFactory" interface which is missing in my app.
1) https://stackoverflow.com/questions/9518394/signalr-maintaining-user-connection-ids
2) http://www.kevgriffin.com/maintaining-signalr-connectionids-across-page-instances/
Before this I was using SignalR alpha version and in that I have manage this issue using "IConnectionIdPrefixGenerator". But in new version this interface is also missing.
Can anyone guide me How to fix this issue proper way?
There is no longer any recommended pattern for setting or maintaining the connection id of a SignalR client in 1.0. I would suggest making single user groups in lieu of ConnectionIds.
SignalR 1.0 beta connection factory
If you don't need to use a custom string to address messages sent to a client and you can trust your clients, you can include metadata inside the query string of each SignalR request.
How to parameterize a SignalR route?

Resources