如何在 class [ASP CORE 2.2] 中注入强类型的 signalR hub]
How to inject strongly typed signalR hub in a class [ASP CORE 2.2]
我想在服务中注入强类型集线器,但我不喜欢 Microsoft 示例中的某些东西 - https://docs.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-2.2(注入强类型 HubContext)
public class ChatController : Controller
{
public IHubContext<ChatHub, IChatClient> _strongChatHubContext { get; }
public ChatController(IHubContext<ChatHub, IChatClient> chatHubContext)
{
_strongChatHubContext = chatHubContext;
}
public async Task SendMessage(string message)
{
await _strongChatHubContext.Clients.All.ReceiveMessage(message);
}
}
在此示例中,ChatHub
耦合到 ChatController
。
所以我想注入集线器本身
使用通用接口参数定义,并且不会在我的服务中定义它的具体实现。
这是示例代码
public interface IReportProcessingClient
{
Task SendReportInfo(ReportProgressModel report);
}
public class ReportProcessingHub : Hub<IReportProcessingClient>
{
public async Task SendMessage(ReportProgressModel report)
{
await Clients.All.SendReportInfo(report);
}
}
public class ReportInfoHostedService : IHostedService, IDisposable
{
private readonly Hub<IReportProcessingClient> _hub;
private readonly IReportGenerationProgressService _reportService;
public ReportInfoHostedService(Hub<IReportProcessingClient> hub, IReportGenerationProgressService reportService)
{
_hub = hub;
_reportService = reportService;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_reportService.SubscribeForChange(async x =>
{
await _hub.Clients.All.SendReportInfo(x);
});
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public void Dispose()
{
}
}
这种方法显然需要在 Startup.cs 中额外注册集线器,因为它不会被 Microsoft 提供的上下文 api 调用。
services.AddSingleton<Hub<IReportProcessingClient>, ReportProcessingHub>();
app.UseSignalR(route => {
route.MapHub<ReportProcessingHub>("/reportProcessingHub");
});
在集线器尝试向客户端发送消息之前,一切都已完成并正在工作。然后我得到异常
_hub.Clients.All threw an exception of System.NullReferenceException: 'Object reference not set to an instance of an object.'
总结一下:
1.这是注入强类型集线器的正确方法吗?我做错了什么(例如,集线器在服务中的注册错误,app.UseSingleR
的错误使用)?
2。如果不是,正确的做法是什么?
注意:
我知道注入 IHubContext<Hub<IReportProcessingClient>>
有更简单的方法,但这对我来说不是解决方案,因为我必须调用作为 string
参数传递的集线器方法名称。
I want to ... and no concrete implementation of it will be defined in my service
如果你不想公开具体的集线器实现,你至少应该公开一个基础class或接口。但是,由于集线器应该继承自 Hub
class,我们不能在这里使用接口。因此,让我们创建一个 public 基础集线器 ReportProcessingHubBase
以及一个内部具体 ReportProcessingHub
:
public abstract class ReportProcessingHubBase : Hub<IReportProcessingClient>
{
public abstract Task SendMessage(ReportProgressModel report);
}
// the concrete hub will NOT be exposed
internal class ReportProcessingHub : ReportProcessingHubBase
{
public override async Task SendMessage(ReportProgressModel report)
{
await Clients.All.SendReportInfo(report);
}
}
确保您已经注册了两个相关服务:
services.AddScoped<ReportProcessingHubBase, ReportProcessingHub>();
services.AddHostedService<ReportInfoHostedService>();
- 确保您正在映射 Base Hub(最重要!):
endpoints.MapHub<ReportProcessingHubBase>("/report");
最后注入IHubContext<ReportProcessingHubBase,IReportProcessingClient>
即可得到base hub :
public class ReportInfoHostedService : IHostedService, IDisposable
{
private readonly IHubContext<ReportProcessingHubBase,IReportProcessingClient> _hub;
public ReportInfoHostedService(IHubContext<ReportProcessingHubBase,IReportProcessingClient> hub)
{
_hub = hub;
}
...
}
现在,您可以以强类型方式调用 hub 方法了。
我想在服务中注入强类型集线器,但我不喜欢 Microsoft 示例中的某些东西 - https://docs.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-2.2(注入强类型 HubContext)
public class ChatController : Controller
{
public IHubContext<ChatHub, IChatClient> _strongChatHubContext { get; }
public ChatController(IHubContext<ChatHub, IChatClient> chatHubContext)
{
_strongChatHubContext = chatHubContext;
}
public async Task SendMessage(string message)
{
await _strongChatHubContext.Clients.All.ReceiveMessage(message);
}
}
在此示例中,ChatHub
耦合到 ChatController
。
所以我想注入集线器本身 使用通用接口参数定义,并且不会在我的服务中定义它的具体实现。 这是示例代码
public interface IReportProcessingClient
{
Task SendReportInfo(ReportProgressModel report);
}
public class ReportProcessingHub : Hub<IReportProcessingClient>
{
public async Task SendMessage(ReportProgressModel report)
{
await Clients.All.SendReportInfo(report);
}
}
public class ReportInfoHostedService : IHostedService, IDisposable
{
private readonly Hub<IReportProcessingClient> _hub;
private readonly IReportGenerationProgressService _reportService;
public ReportInfoHostedService(Hub<IReportProcessingClient> hub, IReportGenerationProgressService reportService)
{
_hub = hub;
_reportService = reportService;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_reportService.SubscribeForChange(async x =>
{
await _hub.Clients.All.SendReportInfo(x);
});
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public void Dispose()
{
}
}
这种方法显然需要在 Startup.cs 中额外注册集线器,因为它不会被 Microsoft 提供的上下文 api 调用。
services.AddSingleton<Hub<IReportProcessingClient>, ReportProcessingHub>();
app.UseSignalR(route => {
route.MapHub<ReportProcessingHub>("/reportProcessingHub");
});
在集线器尝试向客户端发送消息之前,一切都已完成并正在工作。然后我得到异常
_hub.Clients.All threw an exception of System.NullReferenceException: 'Object reference not set to an instance of an object.'
总结一下:
1.这是注入强类型集线器的正确方法吗?我做错了什么(例如,集线器在服务中的注册错误,app.UseSingleR
的错误使用)?
2。如果不是,正确的做法是什么?
注意:
我知道注入 IHubContext<Hub<IReportProcessingClient>>
有更简单的方法,但这对我来说不是解决方案,因为我必须调用作为 string
参数传递的集线器方法名称。
I want to ... and no concrete implementation of it will be defined in my service
如果你不想公开具体的集线器实现,你至少应该公开一个基础class或接口。但是,由于集线器应该继承自
Hub
class,我们不能在这里使用接口。因此,让我们创建一个 public 基础集线器ReportProcessingHubBase
以及一个内部具体ReportProcessingHub
:public abstract class ReportProcessingHubBase : Hub<IReportProcessingClient> { public abstract Task SendMessage(ReportProgressModel report); } // the concrete hub will NOT be exposed internal class ReportProcessingHub : ReportProcessingHubBase { public override async Task SendMessage(ReportProgressModel report) { await Clients.All.SendReportInfo(report); } }
确保您已经注册了两个相关服务:
services.AddScoped<ReportProcessingHubBase, ReportProcessingHub>(); services.AddHostedService<ReportInfoHostedService>();
- 确保您正在映射 Base Hub(最重要!):
endpoints.MapHub<ReportProcessingHubBase>("/report");
最后注入
IHubContext<ReportProcessingHubBase,IReportProcessingClient>
即可得到base hub :public class ReportInfoHostedService : IHostedService, IDisposable { private readonly IHubContext<ReportProcessingHubBase,IReportProcessingClient> _hub; public ReportInfoHostedService(IHubContext<ReportProcessingHubBase,IReportProcessingClient> hub) { _hub = hub; } ... }
现在,您可以以强类型方式调用 hub 方法了。