依赖注入到 SignalR Hub (dotnet core 3.1)

Dependency Injection into SignalR Hub (dotnet core 3.1)

我正在使用 SignalR,但在让 DI 进入 SignalR 集线器时遇到问题。我假设它会使用现有的 dotnet 核心 DI 框架,但事实似乎并非如此。我不断得到

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)

我注册了单例:

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

而且我在集线器的构造函数中有依赖项:

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

文档表明这应该有效。有什么想法吗?

你的问题不是很清楚,但我会尝试回答你如何正确注册你的集线器并使用 DI 注入它。首先,您在启动时添加集线器:

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

所以这会将您的集线器添加为 transient。然后你可以像这样注入你的集线器:

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

还有一些注意事项:

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.

找到问题了。我正在使用 Unity Container,结果发现容器没有像 OOTB DI 框架那样获得 ILogger auto-registered。我通过使用 Immediate Window 尝试解析 IHubClientHelper 发现了这一点,它最终告诉我它无法解析 ILogger (No public constructor is available for type Microsoft.Extensions.Logging.ILogger.)。在快速 google 之后,我找到了 this,并通过将以下内容添加到我在 Startup.cs 中的 ConfigureContainer 方法中,我再次 运行。

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