In SignalR 2 you could do something like this (taken from my blog):
var stockTickerHub = GlobalHost.ConnectionManager.GetHubContext<StockTickerHub>();
That allows you to get a reference to the SignalR hub from outside the hub (e.g. from a stock ticker thread).
This does not seem to be available in SignalR 3. How do you achieve the equivalent functionality in the new and upcoming SignalR?
I asked the same thing to the creator of SignalR, David Fowler on Jabbr, a forum where the creators of SignalR and the architects of ASP.NET 5 hang on from time to time, and his answer to this question was to use dependency injection.
While I haven't tried it yet with SignalR 3, I am pretty sure you can inject an instance of ConnectionManager that implements IConnectionManager in your class, and use it just like you would use GlobalHost to resolve your hub context.
Again, I have not done this with SignalR3, but I hope this will get you a little closer to finding a solution.
I put together a sample for using SignalR 2 with Autofac. (In this repo I use Autofac to inject dependencies in my hub, but also to inject instances of a ConnectionManager in other classes to get the hub context).
Hope this helps. Best of luck!
Dependency injection is indeed the way and works.
Example:
public class ChatController : Controller
{
readonly IConnectionManager _connectionManager;
public ChatController(IConnectionManager connectionManager)
{
_connectionManager = connectionManager;
}
public IActionResult Chat(string message)
{
IHubContext context = _connectionManager.GetHubContext<ChatHub>();
IConnection connection = _connectionManager.GetConnectionContext<PersistentConnection>().Connection;
context.Clients.All.NewMessage(message);
return new EmptyResult();
}
}
From every example I have seen and the few SignalR 3 apps I have implemented, you no longer have a strongly typed reference to your hub. The current methodology connects to a hub via the hub's name and URL. The On generic method creates a subscription to broadcasts from that hub and the method name you provide.
HubConnection connection = new HubConnection(ServerURL);
IHubProxy hubProxy = connection.CreateHubProxy("StockTickerHub");
hubProxy.On<StockTickerMessage>("[Your method name here]", msg => {
//your UI update logic here
});
Related
There are other questions on the topic but the solutions there don't work for me. I guess is a small detail on the syntax since it changes a bit from version to version..
I created a SignalR hub on a blazor hosted WASM, and I want to be able to send messages from my GameController and not only from the hub itself.
My GameHub inherits from Hub, and I to get a hub context to send a message. The nI guet the no valid conversion from GameHub to IHub.
I tried to implement IHub insted of inheriting from Hub but then it ask to implement other methods I have no clue about and I've never seen in other tutorials..
Here's a picture with the error message and the GameHub declaration:
I use IHubContext<...> like this:
public ChatService(IHubContext<ChatHub> chatHub, IStorageService storageService)
{
this.chatHub = chatHub;
this.storageService = storageService;
}
This service is provided for injection as well.
I've been pouring through documentation and forums trying to understand SignalR but I'm pretty stuck.
What I'm trying to achieve is, in a chat application: store messages outside of the Hub so that each time a user joins the chat, they can see all messages that had been sent before they joined.
So it seemed like an external class was the way to do that so I got that working with dependency injection
In ChatHub.cs
namespace SignalRChat.Hubs
{
public class ChatHub: Hub
{
public IChatStorage _chatStorage;
public ChatHub(IChatStorage chatStorage)
{
_chatStorage = chatStorage;
}
// and so on
And I have a method in ChatHub to send a message to chatStorage, but I'm confused on how to send back a list of all messages from chatStorage to ChatHub, or even to JavaScript. It seemed like a Controller was the way to do that but I'm not sure how to call the controller's methods:
namespace SignalRChat.Controllers
{
public class ChatController: Controller
{
private IHubContext<ChatHub> _hubContext;
public ChatController(IHubContext<ChatHub> hubContext)
{
_hubContext = hubContext;
}
public void Send(List<Message> messages)
{
// to do: something where chatStorage calls this method, then this
// method uses _hubContext.Clients.All.SendAsync
// But, how do I even call Send()???
}
}
}
Fundamentally I just don't understand how to wire everything up. SignalR is just really confusing.
I get how the simple server Hub and client JavaScript relationship works. But, then with dependency injection I don't get why
public ChatHub(IChatStorage chatStorage)
{
_chatStorage = chatStorage;
}
works. I didn't change any code to say like new ChatHub(new IChatStorage). Microsoft's docs even say that SignalR only calls default Hub constructors.
In Startup.cs nothing seems to specify that I want to call ChatHub with a new chatStorage:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddSignalR();
services.AddSingleton<IChatStorage, ChatStorage>();
services.AddSingleton<ChatController>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// omitted some default code
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapHub<ChatHub>("/chatHub");
});
}
So first question, how does that work? How does it know to pass an argument to the ChatHub constructor? I understand the services.AddSingleton part, just not how that gets "wired up".
Same thing with a controller class. How does the program know to pass an IHubContext object into its constructor? Where do you specify that?
Finally, how would you go about making this setup work? Current I'm trying to communicate from ChatHub->chatStorage->ChatController->ChatHub. And to do that I'm trying to pass a reference to chatController in chatStorage.
Not sure if it's clear what I'm asking. If anything I'm looking for a clear explanation on how these concepts all work together, rather than a specific solution to my code.
Thank you!
So first question, how does that work? How does it know to pass an argument to the ChatHub constructor? I understand the services.AddSingleton part, just not how that gets "wired up".
When SignalR instantiates your hub instance (the framework controls this, you don't), it will resolve any dependencies specified in your Hub's constructor. This is part of the dependency injection system that's part of .NET (as mentioned in the comments).
Same thing with a controller class. How does the program know to pass an IHubContext object into its constructor? Where do you specify that?
Same idea, but you didn't have to wire up IHubContext, that's something that AddSignalR does.
Finally, how would you go about making this setup work? Current I'm trying to communicate from ChatHub->chatStorage->ChatController->ChatHub. And to do that I'm trying to pass a reference to chatController in chatStorage.
It's not exactly clear to me what you want the interaction to be between the client and the server to both the hub and the controller.
What I'm trying to achieve is, in a chat application: store messages outside of the Hub so that each time a user joins the chat, they can see all messages that had been sent before they joined.
Going back to this original statement I can ask a few clarifying questions:
When the user writes a message in the chat room, are you calling the hub or a controller action (REST API)? That will determine where you need to inject the IChatStorage type. Once you receive a message, you'll stash the message in IChatStorage.
When the user joins the chat, they'll make a call to the server to retrieve all messages. This call will return messages stored in IChatStorage.
Assuming you want to use the hub for everything, you would expose methods on the hub do accomplish this. If you wanted to use REST API calls from the client, then you would use the controller.
This article shows a well-known problem with HttpClient that can lead to socket exhaustion.
I have an ASP.NET Core 3.1 web application. In a .NET Standard 2.0 class library I've added a WCF web service reference in Visual Studio 2019 following this instructions.
In a service I'm using the WCF client the way it's described in the documentation. Creating an instance of the WCF client and then closing the client for every request.
public class TestService
{
public async Task<int> Add(int a, int b)
{
CalculatorSoapClient client = new CalculatorSoapClient();
var resultat = await client.AddAsync(a, b);
//this is a bad way to close the client I should also check
//if I need to call Abort()
await client.CloseAsync();
return resultat;
}
}
I know it's bad practice to close the client without any checks but for the purpose of this example it does not matter.
When I start the application and make five requests to an action method that uses the WCF client and then take a look at the result from netstat I discover open connections with status TIME_WAIT, much like the problems in the article above about HttpClient.
It looks to me like using the WCF client out-of-the-box like this can lead to socket exhaustion or am I missing something?
The WCF client inherits from ClientBase<TChannel>. Reading this article it looks to me like the WCF client uses HttpClient. If that is the case then I probably shouldn't create a new client for every request, right?
I've found several articles (this and this) talking about using a singleton or reusing the WCF client in some way. Is this the way to go?
###UPDATE
Debugging the appropriate parts of the WCF source code I discovered that a new HttpClient and HttpClientHandler were created each time I created a new WCF client which I do for every request.
You can inspect the code here
internal virtual HttpClientHandler GetHttpClientHandler(EndpointAddress to, SecurityTokenContainer clientCertificateToken)
{
return new HttpClientHandler();
}
This handler is used in to create a new HttpClient in the GetHttpClientAsync method:
httpClient = new HttpClient(handler);
This explains why the WCF client in my case behaves just like a HttpClient that is created and disposed for every request.
Matt Connew writes in an issue in the WCF repo that he has made it possible to inject your own HttpMessage factory into the WCF client.
He writes:
I implemented the ability to provide a Func<HttpClientHandler,
HttpMessageHandler> to enable modifying or replacing the
HttpMessageHandler. You provide a method which takes an
HttpClientHandler and returns an HttpMessageHandler.
Using this information I injected my own factory to be able to control the generation of HttpClientHandlers in HttpClient.
I created my own implementation of IEndpointBehavior that injects IHttpMessageHandlerFactory to get a pooled HttpMessageHandler.
public class MyEndpoint : IEndpointBehavior
{
private readonly IHttpMessageHandlerFactory messageHandlerFactory;
public MyEndpoint(IHttpMessageHandlerFactory messageHandlerFactory)
{
this.messageHandlerFactory = messageHandlerFactory;
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
Func<HttpClientHandler, HttpMessageHandler> myHandlerFactory = (HttpClientHandler clientHandler) =>
{
return messageHandlerFactory.CreateHandler();
};
bindingParameters.Add(myHandlerFactory);
}
<other empty methods needed for implementation of IEndpointBehavior>
}
As you can see in AddBindingParameters I add a very simple factory that returns a pooled HttpMessageHandler.
I add this behavior to my WCF client like this.
public class TestService
{
private readonly MyEndpoint endpoint;
public TestService(MyEndpoint endpoint)
{
this.endpoint = endpoint;
}
public async Task<int> Add(int a, int b)
{
CalculatorSoapClient client = new CalculatorSoapClient();
client.Endpoint.EndpointBehaviors.Add(endpoint);
var resultat = await client.AddAsync(a, b);
//this is a bad way to close the client I should also check
//if I need to call Abort()
await client.CloseAsync();
return resultat;
}
}
Be sure to update any package references to System.ServiceModel.* to at least version 4.5.0 for this to work. If you're using Visual Studio's 'Add service reference' feature, VS will pull in the 4.4.4 versions of these packages (tested with Visual Studio 16.8.4).
When I run the applications with these changes I no longer have an open connection for every request I make.
You should consider disposing your CalculatorSoapClient. Be aware that a simple Dispose() is usually not enough, becaue of the implementation of the ClientBase.
Have a look at https://learn.microsoft.com/en-us/dotnet/framework/wcf/samples/use-close-abort-release-wcf-client-resources?redirectedfrom=MSDN, there the problem is explained.
Also consider that the underlying code is managing your connections, sometimes it will keep them alive for later use. Try calling the server a lot of times to see, if there is a new connection for each call, or if the connections are being reused.
The meaning TIME_WAIT is also discussed here:
https://superuser.com/questions/173535/what-are-close-wait-and-time-wait-states
https://serverfault.com/questions/450055/lot-of-fin-wait2-close-wait-last-ack-and-time-wait-in-haproxy
It looks like your client has done everything required to close the connection and is just waiting for the confirmation of the server.
You should not have to use a singleton since the framework is (usually) taking good care of the connections.
I created an issue in the WCF repository in Github and got some great answers.
According to Matt Connew and Stephen Bonikowsky who are authorities in this area the best solution is to reuse the client or the ChannelFactory.
Bonikowsky writes:
Create a single client and re-use it.
var client = new ImportSoapClient();
And Connew adds:
Another possibility is you could create a channel proxy instance from
the underlying channelfactory. You would do this with code similar to
this:
public void Init()
{
_client?.Close();
_factory?.Close();
_client = new ImportSoapClient();
_factory = client.ChannelFactory;
}
public void DoWork()
{
var proxy = _factory.CreateChannel();
proxy.MyOperation();
((IClientChannel)proxy).Close();
}
According to Connew there is no problem reusing the client in my ASP.NET Core web application with potentially concurrent requests.
Concurrent requests all using the same client is not a problem as long
as you explicitly open the channel before any requests are made. If
using a channel created from the channel factory, you can do this with
((IClientChannel)proxy).Open();. I believe the generated client also
adds an OpenAsync method that you can use.
UPDATE
Since reusing the WCF Client also means reusing the HttpClient instance and that could lead to the known DNS problem I decided to go with my original solution using my own implementation of IEndpointBehavior as described in the question.
For SignalR 2.1, how do I maintain a list of Client methods the Server's Hub can call?
According to Differences between SignalR and ASP.NET Core SignalR - Hub Proxies, "Hub proxies are no longer automatically generated."
Is there an existing solution to maintain a list of Client methods the Server's Hub can call?
Looking for a solution that defines Client methods to be called by Server Hub before we decide to roll our own with Code Generation.
Looks like Hub and IHubContext take T type parameter for the type of client that you can make an interface for. Can't find any documentation specific to dotnet core other than the source code and comments but look like this is a carry over from .net
https://blog.scottlogic.com/2014/08/08/signalr-typed.html -> "Calling client hubs - New and Improved"
public interface IMyHubClient
{
void Ping();
}
public class MyHub : Hub<IMyHubClient>
{
...
}
What should be the Autofac 3.5 configuration for Asp.net Mvc 5.2, SignalR 2.1, MS Owin (Katana) 3.0? Is there less complex way to register Autofac resolvers (there is two of them now)? Or why ILifetimeScope is not visible for my hub?
The exception:
Autofac.Core.DependencyResolutionException: An exception was thrown
while invoking the constructor 'Void .ctor(Autofac.ILifetimeScope)' on
type 'OperatorHub'. --->
No scope with a Tag matching
'AutofacWebRequest' is visible from the scope in which the instance
was requested. This generally indicates that a component registered as
per-HTTP request is being requested by a SingleInstance() component
(or a similar scenario.) Under the web integration always request
dependencies from the DependencyResolver.Current or
ILifetimeScopeProvider.RequestLifetime, never from the container
itself. (See inner exception for details.) --->
Autofac.Core.DependencyResolutionException: No scope with a Tag
matching 'AutofacWebRequest' is visible from the scope in which the
instance was requested. This generally indicates that a component
registered as per-HTTP request is being requested by a
SingleInstance() component (or a similar scenario.) Under the web
integration always request dependencies from the
DependencyResolver.Current or ILifetimeScopeProvider.RequestLifetime,
never from the container itself.
In my OwinStartup (see autofac + mvc owin and autofac + signalr in owin):
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// ... registration. There is .InstancePerRequest() and .SingleInstance()
Autofac.Integration.Mvc.RegistrationExtensions.RegisterControllers(builder,typeof(MvcApplication).Assembly);
Autofac.Integration.SignalR.RegistrationExtensions.RegisterHubs(builder, Assembly.GetExecutingAssembly());
var container = builder.Build();
// 1st resolver
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
app.UseAutofacMiddleware(container);
app.UseAutofacMvc();
// yet the 2nd resolver!
app.MapSignalR(new HubConfiguration { Resolver = new Autofac.Integration.SignalR.AutofacDependencyResolver(container) });
}
The hub:
public class OperatorHub : Hub
{
public OperatorHub(ILifetimeScope hubLifetimeScope)
{
hubLifetimeScope.BeginLifetimeScope();
// ...
// HERE IT FALLS. The IMyService relates to MyDbContext (see below)
var myservice = hubLifetimeScope.Resolve<IMyService>();
}
}
UPDATE
The breaking component registration (EF Context:
builder.RegisterType<MyDbContext>().AsSelf().As<DbContext>().InstancePerRequest("OwinLifetimeScope");
In short the bug is the MyDbContext is not in the 'root' lifetime scope which is passed to OperatorHub constructor.
UPDATE 2
The solution with the help of #TravisIllig is to register the MyDbContext service using .InstancePerLifetimeScope() and to create the one in the hub. Another lifetime scope would be created for http request in asp mvc. Create help at Sharing Dependencies Across Apps Without Requests.
Also the hub should not dispose the given scope as it is the root one which results in ObjectDisposedException on the second run.
There is an FAQ on handling this exact exception on the Autofac doc site. The problem stems from the fact you're using InstancePerRequest in conjunction with SignalR, which, also per the documentation:
Due to SignalR internals, there is no support in SignalR for per-request lifetime dependencies.
You do appear to have looked at the Autofac SignalR docs as I see you've injected a lifetime scope to help you manage instance lifetimes, but that doesn't give you per-request lifetime scopes, it just gives you a hub lifetime scope. I might suggest revisiting that doc for a refresher.
The FAQ I mentioned, in conjunction with the SignalR integration docs, should point you to the right solution for your app. Many people simply switch their registrations from InstancePerRequest to InstancePerLifetimeScope but I strongly encourage you to read the FAQ and check out your options before just jumping to that decision. It may be the right choice, but it may not be - it depends on how your app works internally.