使用IHubContext创建类实例

时间:2019-06-03 19:43:24

标签: c# signalr

我有以下课程。我需要创建此调用的实例,以便可以从我尝试过的静态方法中调用非静态方法

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

2 个答案:

答案 0 :(得分:2)

使用ASP核心

似乎您正在将集线器与一些长期运行的东西(计时器)混合在一起。您不应该这样做,也不需要集线器内部的IHubContext,因为此IHubContext仅允许您访问集线器而实际上没有集线器实例。
您应该从Hub类中删除IHubContext,而应将IHubContext注入到长时间运行的Background任务中。您可以阅读有关实现后台任务here的更多信息。使用此系统,您将不会使用计时器,而会使用某种延迟(可能),但这并不难更改,因此不必为此担心。

在那之后,您只需要做一些事情就可以使其协同工作。
 -将Hub-classname终止在Hub上(已完成)。
 -将Hub-class放置在名为Hubs的文件夹中。
 -从Hub类中删除IHubContext,然后将其添加到后台任务中,方法与您当前在Hub类中使用的方法相同。

您现在可以使用IServiceCollection.AddHostedService<YourBackgroundService>()将后台任务添加到系统中。您可以在ConfigureServices类的Startup方法中执行此操作。

您现在可以在后台服务内部使用IHubContext访问集线器,它将能够执行所有基于超时/计时器的任务。在集线器本身周围有逻辑不应该放在集线器类中。

我希望这会有所帮助。如果您不使用ASP Core,那么我将继续删除此答案。

编辑:
您的后台服务可能看起来像这样:

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

如果将stopToken取消,则将其添加到Task.Delay会抛出TaskCanceledException。只需停止循环并在发生这种情况时返回即可。

我还建议您使用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类的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()即可调用中心中定义的方法(我在完整解决方案中将该方法添加到了中心中)。如果您使用参数/返回值,它们都将在json和c#对象之间转换。

这就是您需要做的,而不是那么多:)。

我们要到达那里。现在,您已经删除了所有不应该用于远程处理的非公开方法,这非常好。这些方法与sql有关,因此您应该将它们放在一个单独的类中,该类的好名字告诉它是用于检索数据。
假设您已经定义并填充了您的课程。您现在需要做的是将其注册到DI系统。使用db-stuff,您通常需要一个单例,但如果不了解类包含/执行的操作,则无法决定。
您可以在Startup-class的ConfigureServices方法内注册该类。 services.AddSingleton<YourSQLThingy>();

现在,DI系统知道您的班级,您只需将其注入到任何需要的地方即可。为此,将它放到Hubs构造函数中(我更新了“完整”解决方案”)。DI系统将在激活您的集线器时自动找到注册的类并将其注入。请注意,由于您在此处使用单例,它将永远是注入的同一实例!

答案 1 :(得分:0)

如果您使用的是ASP.NET Core,则可以使用依赖项注入来获取IHubContext的实例。

如果您使用的是ASP.NET 4.x,则可以通过GlobalHost访问IHubContext。

var hubContext = GlobalHost.ConnectionManager.GetHubContext<SomeHub>();