只通过接口注入SignalR Hub

3
最近我开始了一个项目,使用Ardalis Clean Architecture作为模板。一切都很好,但当signalR出现在我的项目中时,我就无法弄清楚它。我正在尝试注入我的hub实现的接口并调用它的方法,但每次调用它时都会抛出NullReferenceException异常,似乎所有的signalR组件都在这个注入的接口中为空。使用AutoFac注册了所有hubs并注册了它们的接口。我正在尝试避免在核心层中强制引用signalR包的情况。
核心层:
public class UpdateTimerNotificationHandler : INotificationHandler<UpdateTimerNotification>
{
    private readonly ITimerHub _timerHub;
    public UpdateTimerNotificationHandler(ITimerHub timerHub)
    {
        _timerHub = timerHub;
    }

    public Task Handle(UpdateTimerNotification notification, CancellationToken cancellationToken)
    {
        return _timerHub.UpdateTimerAsync(notification);
    }
}

public interface ITimerHub
{
    Task UpdateTimerAsync(UpdateTimerNotification updateTimerNotification);
}

基础设施层:

public class TimerHub : Microsoft.AspNetCore.SignalR.Hub, ITimerHub
{
    private readonly IAccountRepository _accountRepository;
    public TimerHub(IAccountRepository accountRepository)
    {
        _accountRepository = accountRepository;
    }

    public Task UpdateTimerAsync(UpdateTimerNotification updateTimerNotification)
    {
        return Clients.All.SendAsync("UpdateTimer", updateTimerNotification);
    }
}

private void RegisterHubs(ContainerBuilder builder)
    {
        foreach (var assembly in _assemblies)
        {
            builder.RegisterHubs(assembly);
        }
        builder.RegisterType<TimerHub>().As<ITimerHub>();
    }

Web层:

builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder =>
{
    containerBuilder.RegisterModule(new DefaultCoreModule());
    containerBuilder.RegisterModule(
        new DefaultInfrastructureModule(builder.Environment.EnvironmentName == "Development"));
});

builder.Logging.ClearProviders();
builder.Logging.AddConsole();

var app = builder.Build();
GlobalHost.DependencyResolver = new AutofacDependencyResolver(app.Services.GetAutofacRoot());

我尝试手动注册中心,但没有成功,问题仍然存在。


这不是批评,而是建议。我来告诉你为什么你无法理解它。因为他的模板不符合你的需求,而且使用它没有意义。你很快就会发现,人们在使用这些概念时,95% 的时间都不会有意义。做个好事吧,放弃那个模板,重新开始。你不需要默认实现 SOLID 和 Repository 模式之类的东西,也不需要在设计代码时围绕它们进行。当问题出现时,你可以实现所需的功能。你的应用程序中没有这些用例。SignalR 接口有什么意义呢? - Train
@Train 我想学习有关干净架构和SOLID的知识,所以我选择了这个模板。之前我常常在一个大型项目中构建所有的Web应用程序,但随着服务的增长等因素,项目已经变得非常臃肿。因此,我认为选择一些新的东西可以教会我如何将各种内容组织成不同的项目。关于你提到的Signalr的问题,我希望拥有强类型的Hub并能够通过接口将其注入到服务中。 - ISmellYu
2个回答

1
好消息是SignalR已经实现了IHubContext<T>。在您的情况下,您不需要注入ITimerHub接口。如果您的TimerHub已经实现了ITimerHub,那就足够了。在您的情况下,它会像这样:
public class HomeController : Controller
{
    private readonly IHubContext<TimerHub> _hubContext;

    public HomeController(IHubContext<TimerHub> hubContext)
    {
        _hubContext = hubContext;
    }
}

您没有展示您的startup.cs类。
    public void ConfigureServices(IServiceCollection services)
    {
        ...
        services.AddSignalR();
        ...
    }

并且

 public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
 {
     ...
     app.MapHub<TimerHub>("/yourEndPointGoesHere");
 }

如果你真的想要,我不建议这样做,但你可以在[这里查看][1]一个使用IHubContext在通用代码中的示例。
我知道你正在尝试学习新东西。是的,解耦应用程序很重要,所以你朝着正确的方向前进了。然而,我不建议你采取这种方法。他的方法并不适用于99%的项目。让我解释一下我的观点。不要被他的视频和博客中的流行词汇所吸引。重要的是要理解这些原则是主观的,取决于你的应用程序。
1. 你的应用程序中没有15000个类、服务、视图和N层等等。 2. 你不需要领域驱动方法的灵活性。我见过大型的、有数百万行代码的25年历史的项目。让我告诉你,你不会像他所说的那样随意更换数据层。在一个大型项目中,没有"容易"的方法来做到这一点。将其放入 Repos 和数据访问层并不能真正帮助你。你可以将其放入数据访问层或者服务中。你仍然需要测试150000行代码。它对我唯一有用的时候是当我有4个数据源,所有数据源都需要聚合来自4个源的信息的getBy...函数时。你也不需要它来进行单元测试。只需在单元测试中创建一个模拟变量,无需模拟数据库连接。我发现将单元测试实际连接到数据库更有用,即使它是一个依赖项,它实际上是有用的。 3. 他自己说过:"你可以选择一个最小化的API,然后从那里开始",这就是你应该做的。在没有代码的项目中使用SOLID和Repos有什么意义呢?例如,SOLID中的I代表接口的实现。接口有两个作用: A. 告诉你的应用程序它应该做什么和不应该做什么。所以,你正在强制执行什么可能会破坏或需要这种抽象? B. 解耦应用程序。你在哪里注入了3个以上不同的类,在同一段代码中基于类型使用相同的DoSomething()
他还涉及其他一些只适用于有500个不同事物的情况,而在他的情况下,这仍然是过度设计。
如果你想分解它,你可以采取简单的方法。
-MainApiProject
-ServicesProject (you can also put interfaces in here)
-InterfacesProject(if you need them between multiple projects and have a lot of them)
-UtilitiesProject

看他正在做什么,如果你发现需要它,请拿走。 我可以继续讲下去,但这篇文章已经够长了。

[1]: https://learn.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-6.0

你的回答为我提供了关于项目结构和何时使用模板(如Clean Architecture)的大量有价值的信息,就像是一桶冷水浇在头上。当我在给我的应用程序添加功能时,我不得不花费1个小时(有时更长时间)来思考如何将我的功能融入到项目中。最终,我创建了“Communication”项目与MediatR,“Identity”项目以及你的简单方法。感谢你详细的回答。 - ISmellYu
很高兴能够帮到您。我知道学习这些概念有时会让人感到不知所措。 - Train

0

以下是对我有效的使用(Hub和Interface):

private readonly IHubContext<MessageHub,IMsgHub> HubContext;

public BMController(IHubContext<MessageHub, IMsgHub> ctx)
{
    HubContext = ctx;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接