使用 IHubContext 创建 class 个实例
Creating class instance with IHubContext
我有以下class。我需要创建此调用的实例,以便我可以从静态方法调用非静态方法,我已经尝试过
EstablishmentHub establishmenthub = new EstablishmentHub();
但这行不通,因为这一行:
public EstablishmentsHub(IHubContext<EstablishmentsHub> hubcontext)
{
HubContext = hubcontext;
}
我可以创建一个实例吗?
public class EstablishmentsHub : Hub
{
public readonly static System.Timers.Timer _Timer = new System.Timers.Timer();
public EstablishmentsHub(IHubContext<EstablishmentsHub> hubcontext)
{
HubContext = hubcontext;
}
private IHubContext<EstablishmentsHub> HubContext
{
get;
set;
}
}
我正在尝试使用以下方法在启动时启动计时器功能
static EstablishmentsHub()
{
_Timer.Interval = 10000;
_Timer.Elapsed += TimerElapsed;
_Timer.Start();
}
但它抱怨 TimeElapsed(非静态字段需要对象引用)
我不想让 TimeElasped 成为静态的,因为它下面有一些函数是非静态的
void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
broadcaststatus("ayl-3",10);
}
如果您正在使用 ASP.NET 核心,您可以使用依赖注入来获取 IHubContext 的实例。
如果您正在使用 ASP.NET 4.x,您可以使用 GlobalHost 访问 IHubContext。
var hubContext = GlobalHost.ConnectionManager.GetHubContext<SomeHub>();
假设 ASP 核心
您似乎将集线器与一些 long-running 东西(计时器)混合在一起。您不应该那样做,也不需要集线器内的 IHubContext,因为此 IHubContext 只允许您访问集线器而实际上没有集线器的实例。
您应该从 Hub class 中删除 IHubContext,而是将该 IHubContext 注入 long-running 后台任务。您可以阅读有关实施后台任务的更多信息 here。有了这个系统,你就不会使用计时器,而是会有某种延迟(可能),但这并不难改变,所以不用担心。
之后,您只需要做一些事情就可以让它协同工作。
- 在集线器上结束 Hub-classname(已经完成)。
- 将 Hub-class 放在名为 Hubs 的文件夹中。
- 从 Hub-class 中删除 IHubContext 并将其添加到后台任务,就像您当前在 Hub-class.
中一样
您现在可以使用 IServiceCollection.AddHostedService<YourBackgroundService>()
将后台任务添加到您的系统中。您可以在 Startup
-class 中的 ConfigureServices
方法中执行此操作。
您现在可以在后台服务中使用 IHubContext 访问集线器,它将能够执行所有基于 timeout/timer 的操作。围绕集线器本身的逻辑不应放在 Hub-class 内。
希望对您有所帮助。如果您不使用 ASP 核心,我将继续并删除此答案。
编辑:
您的 background-service 可能看起来像这样:
public class SomeTimedService : Microsoft.Extensions.Hosting.BackgroundService
{
private IHubContext<EstablishmentsHub> _hubContext;
public SomeTimedService(IHubContext<EstablishmentsHub> hubcontext)
{
_hubContext = hubcontext;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await _hubContext.Clients.All.BroadcastStatus("ayl-3", 10);
try
{
await Task.Delay(10000, stoppingToken);
}
catch (TaskCanceledException) {
return;
}
}
}
}
将 stoppingToken 添加到 Task.Delay
将抛出一个 TaskCanceledException
如果它被取消。发生这种情况时,只需停止循环和 return 即可。
我还建议您使用 strongly-typed hubs,因为它们有很多好处。
一个完整的解决方案(包括您所写的关于您的应用程序的所有内容)如下所示:
// background service for sending a (currently hardcoded) status every 10 seconds to every client
public class EstablishmentsService : Microsoft.Extensions.Hosting.BackgroundService
{
private IHubContext<EstablishmentsHub, IEstablishmentsClient> _hubContext;
public EstablishmentsService(IHubContext<EstablishmentsHub, IEstablishmentsClient> hubcontext)
{
_hubContext = hubcontext;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await _hubContext.Clients.All.ReceiveStatus("ayl-3", 10);
try
{
await Task.Delay(10000, stoppingToken);
}
catch (TaskCanceledException) {
return;
}
}
}
}
// the client of the hub. Here you place methods which should be called by the application.
public interface IEstablishmentsClient
{
Task ReceiveStatus(string someString, int someInt);
}
// the hub itself. You'd place methods here which should be called from the client
public class EstablishmentsHub : Hub<IEstablishmentsClient>
{
private readonly YourSQLThingy _sqlThingy;
public EstablishmentsHub(YourSQLThingy sQLThingy)
{
_sqlThingy = sQLThingy;
}
public SomeObject GetFirstObject()
{
return _sqlThingy.Whatever.First();
}
public void SomeHubMethod()
{
// do something here
}
}
在Startup.ConfigureServices(IServiceCollection services)
内你需要调用:
services.AddHostedService<EstablishmentsService>();
当然你也必须添加js。在我的项目中,我使用一些代码在 Startup-class 的 Configure
方法中注册了 signalR-url:
app.UseSignalR(routes =>
{
routes.MapHub<EstablishmentsHub>("/establishmentsSignalR");
});
在 js 中,您现在可以使用相同的 url 获取集线器(几乎可以肯定还有其他没有 url 的方法,但我是这样做的):
var connection = new signalR.HubConnectionBuilder().withUrl("/establishmentsSignalR").build();
获得此连接后,您可以对所有客户端方法使用 connection.on(xxxx, ..)
,对所有集线器方法使用 connection.xxxx(..)
。
在此示例中,您将执行以下操作:
connection.on("ReceiveStatus", function (someString, someInt) {
// do whatever with those two values
// if it's a bigger object it will simply be converted to a jsonObject
});
现在您已经设置了客户端发生的情况。您现在可以使用 connection.start();
开始连接。
请注意,在 js 中,您只需使用 connection.SomeHubMethod()
即可调用集线器中定义的方法(我在完整解决方案中将该方法添加到集线器中)。如果您使用 parameters/return 值,它会在 json 和 c# 对象之间全部转换。
这就是您需要做的全部,没那么多:)。
我们到了。您现在已经删除了不应与远程处理一起使用的所有 non-public 方法,非常好。这些方法与 sql 相关,因此您应该将它们放在一个单独的 class 中,并使用一个好听的名称来表明它用于检索数据。
假设您定义并填充了 class。您现在需要做的是将其注册到 DI-system。使用 db-stuff 你通常想要一个单例,但我不能在没有看到 class contains/does.
的情况下做出决定
您可以在 Startup-class 中的 ConfigureServices
方法中注册 class。 services.AddSingleton<YourSQLThingy>();
现在 DI-system 知道你的 class 并且你可以简单地将它注入任何你想要的地方。为此,将其放入 Hubs 构造函数(我更新了 "full" 解决方案”)。DI-system 将在激活您的集线器时自动找到已注册的 class 并像那样注入它。请注意因为你在这里使用的是单例,所以它总是被注入同一个实例!
我有以下class。我需要创建此调用的实例,以便我可以从静态方法调用非静态方法,我已经尝试过
EstablishmentHub establishmenthub = new EstablishmentHub();
但这行不通,因为这一行:
public EstablishmentsHub(IHubContext<EstablishmentsHub> hubcontext)
{
HubContext = hubcontext;
}
我可以创建一个实例吗?
public class EstablishmentsHub : Hub
{
public readonly static System.Timers.Timer _Timer = new System.Timers.Timer();
public EstablishmentsHub(IHubContext<EstablishmentsHub> hubcontext)
{
HubContext = hubcontext;
}
private IHubContext<EstablishmentsHub> HubContext
{
get;
set;
}
}
我正在尝试使用以下方法在启动时启动计时器功能
static EstablishmentsHub()
{
_Timer.Interval = 10000;
_Timer.Elapsed += TimerElapsed;
_Timer.Start();
}
但它抱怨 TimeElapsed(非静态字段需要对象引用)
我不想让 TimeElasped 成为静态的,因为它下面有一些函数是非静态的
void TimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
{
broadcaststatus("ayl-3",10);
}
如果您正在使用 ASP.NET 核心,您可以使用依赖注入来获取 IHubContext 的实例。
如果您正在使用 ASP.NET 4.x,您可以使用 GlobalHost 访问 IHubContext。
var hubContext = GlobalHost.ConnectionManager.GetHubContext<SomeHub>();
假设 ASP 核心
您似乎将集线器与一些 long-running 东西(计时器)混合在一起。您不应该那样做,也不需要集线器内的 IHubContext,因为此 IHubContext 只允许您访问集线器而实际上没有集线器的实例。
您应该从 Hub class 中删除 IHubContext,而是将该 IHubContext 注入 long-running 后台任务。您可以阅读有关实施后台任务的更多信息 here。有了这个系统,你就不会使用计时器,而是会有某种延迟(可能),但这并不难改变,所以不用担心。
之后,您只需要做一些事情就可以让它协同工作。
- 在集线器上结束 Hub-classname(已经完成)。
- 将 Hub-class 放在名为 Hubs 的文件夹中。
- 从 Hub-class 中删除 IHubContext 并将其添加到后台任务,就像您当前在 Hub-class.
您现在可以使用 IServiceCollection.AddHostedService<YourBackgroundService>()
将后台任务添加到您的系统中。您可以在 Startup
-class 中的 ConfigureServices
方法中执行此操作。
您现在可以在后台服务中使用 IHubContext 访问集线器,它将能够执行所有基于 timeout/timer 的操作。围绕集线器本身的逻辑不应放在 Hub-class 内。
希望对您有所帮助。如果您不使用 ASP 核心,我将继续并删除此答案。
编辑:
您的 background-service 可能看起来像这样:
public class SomeTimedService : Microsoft.Extensions.Hosting.BackgroundService
{
private IHubContext<EstablishmentsHub> _hubContext;
public SomeTimedService(IHubContext<EstablishmentsHub> hubcontext)
{
_hubContext = hubcontext;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await _hubContext.Clients.All.BroadcastStatus("ayl-3", 10);
try
{
await Task.Delay(10000, stoppingToken);
}
catch (TaskCanceledException) {
return;
}
}
}
}
将 stoppingToken 添加到 Task.Delay
将抛出一个 TaskCanceledException
如果它被取消。发生这种情况时,只需停止循环和 return 即可。
我还建议您使用 strongly-typed hubs,因为它们有很多好处。
一个完整的解决方案(包括您所写的关于您的应用程序的所有内容)如下所示:
// background service for sending a (currently hardcoded) status every 10 seconds to every client
public class EstablishmentsService : Microsoft.Extensions.Hosting.BackgroundService
{
private IHubContext<EstablishmentsHub, IEstablishmentsClient> _hubContext;
public EstablishmentsService(IHubContext<EstablishmentsHub, IEstablishmentsClient> hubcontext)
{
_hubContext = hubcontext;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await _hubContext.Clients.All.ReceiveStatus("ayl-3", 10);
try
{
await Task.Delay(10000, stoppingToken);
}
catch (TaskCanceledException) {
return;
}
}
}
}
// the client of the hub. Here you place methods which should be called by the application.
public interface IEstablishmentsClient
{
Task ReceiveStatus(string someString, int someInt);
}
// the hub itself. You'd place methods here which should be called from the client
public class EstablishmentsHub : Hub<IEstablishmentsClient>
{
private readonly YourSQLThingy _sqlThingy;
public EstablishmentsHub(YourSQLThingy sQLThingy)
{
_sqlThingy = sQLThingy;
}
public SomeObject GetFirstObject()
{
return _sqlThingy.Whatever.First();
}
public void SomeHubMethod()
{
// do something here
}
}
在Startup.ConfigureServices(IServiceCollection services)
内你需要调用:
services.AddHostedService<EstablishmentsService>();
当然你也必须添加js。在我的项目中,我使用一些代码在 Startup-class 的 Configure
方法中注册了 signalR-url:
app.UseSignalR(routes =>
{
routes.MapHub<EstablishmentsHub>("/establishmentsSignalR");
});
在 js 中,您现在可以使用相同的 url 获取集线器(几乎可以肯定还有其他没有 url 的方法,但我是这样做的):
var connection = new signalR.HubConnectionBuilder().withUrl("/establishmentsSignalR").build();
获得此连接后,您可以对所有客户端方法使用 connection.on(xxxx, ..)
,对所有集线器方法使用 connection.xxxx(..)
。
在此示例中,您将执行以下操作:
connection.on("ReceiveStatus", function (someString, someInt) {
// do whatever with those two values
// if it's a bigger object it will simply be converted to a jsonObject
});
现在您已经设置了客户端发生的情况。您现在可以使用 connection.start();
开始连接。
请注意,在 js 中,您只需使用 connection.SomeHubMethod()
即可调用集线器中定义的方法(我在完整解决方案中将该方法添加到集线器中)。如果您使用 parameters/return 值,它会在 json 和 c# 对象之间全部转换。
这就是您需要做的全部,没那么多:)。
我们到了。您现在已经删除了不应与远程处理一起使用的所有 non-public 方法,非常好。这些方法与 sql 相关,因此您应该将它们放在一个单独的 class 中,并使用一个好听的名称来表明它用于检索数据。
假设您定义并填充了 class。您现在需要做的是将其注册到 DI-system。使用 db-stuff 你通常想要一个单例,但我不能在没有看到 class contains/does.
的情况下做出决定
您可以在 Startup-class 中的 ConfigureServices
方法中注册 class。 services.AddSingleton<YourSQLThingy>();
现在 DI-system 知道你的 class 并且你可以简单地将它注入任何你想要的地方。为此,将其放入 Hubs 构造函数(我更新了 "full" 解决方案”)。DI-system 将在激活您的集线器时自动找到已注册的 class 并像那样注入它。请注意因为你在这里使用的是单例,所以它总是被注入同一个实例!