简体   繁体   中英

Dependency Injection into SignalR Hub (dotnet core 3.1)

I'm using SignalR and having an issue getting DI to work into a SignalR hub. I assumed it would use the existing dotnet core DI framework, but that doesn't seem to be the case. I keep getting

System.InvalidOperationException: Unable to resolve service for type 'Comcast.Cs.Mercury.Web.Api.IHubClientHelper' while attempting to activate 'mercury_ms_auth.Hubs.AuthenticationHub'.
   at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
   at lambda_method(Closure , IServiceProvider , Object[] )
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubActivator`1.Create()
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnConnectedAsync(HubConnectionContext connection)
   at Microsoft.AspNetCore.SignalR.Internal.DefaultHubDispatcher`1.OnConnectedAsync(HubConnectionContext connection)
   at Microsoft.AspNetCore.SignalR.HubConnectionHandler`1.RunHubAsync(HubConnectionContext connection)

I registered the singleton:

            services.AddSingleton<IHubClientHelper>(new HubClientHelper(loggerFactory.CreateLogger<HubClientHelper>())
            {
                ClientConnectionType = _environment.IsDevelopment()
                    ? ClientConnectionType.ConnectionId
                    : ClientConnectionType.UserId
            });

And I have the dependency coming into the constructor of the hub:

public AuthenticationHub(TelemetryClient telemetryClient, IHubClientHelper clientHelper)
            : base(telemetryClient, clientHelper)
        {
            
        }

Documentation indicates this should work. Any ideas?

Your question is not very clear, but I will try to answer how you correctly register your hub and inject it using DI. First of all you add your hub on startup:

services.AddSignalR(hubOptions =>
{
    hubOptions.ClientTimeoutInterval = TimeSpan.FromSeconds(hostConfiguration.SignalR.ClientTimeoutInterval);
    hubOptions.HandshakeTimeout = TimeSpan.FromSeconds(hostConfiguration.SignalR.HandshakeTimeout);
    hubOptions.KeepAliveInterval = TimeSpan.FromSeconds(hostConfiguration.SignalR.KeepAliveInterval);
    hubOptions.EnableDetailedErrors = hostConfiguration.SignalR.EnableDetailedErrors;
    hubOptions.MaximumReceiveMessageSize = hostConfiguration.SignalR.MaximumReceiveMessageSize;
    hubOptions.StreamBufferCapacity = hostConfiguration.SignalR.StreamBufferCapacity;
});
app.UseSignalR(routes =>
{
    routes.MapHub<YourHub>(/yourHub);
});

So this will add your hub as a transient . Then you can inject your hub like:

private IHubContext<YourHub, IYourHub> YourHub
{
    get
    {
        return this.serviceProvider.GetRequiredService<IHubContext<YourHub, IYourHub>>();
    }
}

And some notes:

SignalR expects the Hub to be created separately for each message. You need to add it as a Transient service if you want your Hub to be in DI. You generally shouldn't resolve the Hub out of DI. If you need to share code between your Hub and some other component, I'd suggest using either IHubContext or putting the shared code in a separate DI service instead.

Found the problem. I'm using Unity Container and turns out the container doesn't get ILogger auto-registered like the OOTB DI framework does. I found that out by using Immediate Window to try to Resolve IHubClientHelper and it finally told me that it couldn't resolve ILogger ( No public constructor is available for type Microsoft.Extensions.Logging.ILogger. ). After a quick google on that, I found this , and by adding the following to my ConfigureContainer method in Startup.cs, I was up and running again.

var logFactory = new LoggerFactory();
container.AddExtension(new LoggingExtension(logFactory));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM