.NET Core 7 SignalR如何使用DI创建基于时间的统计更新

huangapple go评论62阅读模式
英文:

.NET Core 7 SignalR how to create time based stats update using DI

问题

I have the following hub and I'm trying to determine how to set it up to send stats back to the client every 15 seconds using a DI based service to pull the data from my database. I can't seem to wrap my head around how to structure this logic to work.

[Authorize]
public class StatsHub : Hub
{
    public override async Task OnConnectedAsync()
    {
        // this method call obviously won't work due to the paramters, not sure how to structure this
        GetStats();

        return base.OnConnectedAsync();
    }

    private void GetStats([FromServices] IQueueService queueService, [FromServices] TimerManager timerManager)
    {
        timerManager.PrepareTimer(async () => {
            var results = await queueService.GetStats();

            await Clients.All.SendAsync("SendQueueStats", results.Payload);
        }, 15000);            
    }
}

Here is the PrepareTimer method logic:

private Timer _timer;
private AutoResetEvent _autoResetEvent;
private Action _action;

public DateTime TimerStarted { get; set; }
public bool IsTimerStarted { get; set; }

public void PrepareTimer(Action action, int delayMS)
{
    _action = action;
    _autoResetEvent = new AutoResetEvent(false);
    _timer = new Timer(Execute, _autoResetEvent, 1000, delayMS);
    TimerStarted = DateTime.Now;
    IsTimerStarted = true;
}

public void Execute(object stateInfo)
{
    // Execute the action associated with the timer.
    _action();

    // If the timer has been running for less than 120 seconds, return.
    if (!((DateTime.Now - TimerStarted).TotalSeconds > 120))
        return;

    // Set IsTimerStarted to false since the timer has stopped.
    IsTimerStarted = false;

    // Dispose of the timer.
    _timer.Dispose();
}

I can't seem to wrap my head around how to get this to work in the manner I'm hoping - basically running the service method every 15 seconds on the timer and broadcasting the stats out.

英文:

I have the following hub and I'm trying to determine how to set it up to send stats back to the client every 15 seconds using a DI based service to pull the data from my database. I can't seem to wrap my head around how to structure this logic to work.

[Authorize]
public class StatsHub : Hub
{
    public override async Task OnConnectedAsync()
    {
        // this method call obviously won't work due to the paramters, not sure how to structure this
        GetStats();

        return base.OnConnectedAsync();
    }

    private void GetStats([FromServices] IQueueService queueService, [FromServices] TimerManager timerManager)
    {
        timerManager.PrepareTimer(async () => {
            var results = await queueService.GetStats();

            await Clients.All.SendAsync("SendQueueStats", results.Payload);
        }, 15000);            
    }
}

Here is the PrepareTimer method logic

private Timer _timer;
private AutoResetEvent _autoResetEvent;
private Action _action;

public DateTime TimerStarted { get; set; }
public bool IsTimerStarted { get; set; }

public void PrepareTimer(Action action, int delayMS)
{
    _action = action;
    _autoResetEvent = new AutoResetEvent(false);
    _timer = new Timer(Execute, _autoResetEvent, 1000, delayMS);
    TimerStarted = DateTime.Now;
    IsTimerStarted = true;
}

public void Execute(object stateInfo)
{
    // Execute the action associated with the timer.
    _action();

    // If the timer has been running for less than 120 seconds, return.
    if (!((DateTime.Now - TimerStarted).TotalSeconds > 120))
        return;

    // Set IsTimerStarted to false since the timer has stopped.
    IsTimerStarted = false;

    // Dispose of the timer.
    _timer.Dispose();
}

I can't seem to wrap my head around how to get this to work in the manner I'm hoping - basically running the service method every 15 seconds on the timer and broadcasting the stats out.

答案1

得分: 1

以下是您要翻译的内容:

You don't want to run the timer from your hub. You use an IHostedService run the timed operation for you and simply call the hub to send the stats.

For example:

public class TimerService : IHostedService
{
    private readonly IQueueService _queueService;
    private readonly StatsService _statsService;
    private readonly CancellationTokenSource _cts;

    private Timer? _timer = null;

    public TimerService(IQueueService queueService, StatsService statsService)
    {
        _queueService = queueService;
        _statsService = statsService;

        _cts = new CancellationTokenSource();
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _timer = new Timer(SendStats, null, TimeSpan.Zero, TimeSpan.FromSeconds(15));

        return Task.CompletedTask;
    }

    private void SendStats(object? state)
    {
        var stats = _queueService.GetStats();

        Task.WaitAll(_statsService.SendStats(stats, _cts.Token));
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _cts.Cancel();
        
        _timer?.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }
}

Register this as a hosted service and .NET will take care of running it for you.

builder.Services.AddHostedService<TimerService>();

The timer within the service calls the StatsService that calls the clients with the data. This does your heavy lifting of loading the data and then calling the hub to send it out. The IHubContext<T> passed in the constructor is a SignalR interface that allows classes outside of a hub to send messages. More info here

public class StatsService
{
    private readonly IHubContext<StatsHub> _hub;

    public StatsService(IHubContext<StatsHub> hub)
    {
        _hub = hub;
    }

    public async Task SendStats(int[] stats, CancellationToken cancellationToken)
    {
        await _hub.Clients.All.SendAsync("SendQueueStats", stats, cancellationToken);
    }
}

Unless you want to do something specific when the client connects or disconnects to the hub, it can be simplified down to:

[Authorize]
public class StatsHub : Hub
{
}

Information about using IHostedService to run a timed job can be found on Microsoft Learn

英文:

You don't want to run the timer from your hub. You use an IHostedService run the timed operation for you and simply call the hub to send the stats.

For example:

public class TimerService : IHostedService
{
    private readonly IQueueService _queueService;
    private readonly StatsService _statsService;
    private readonly CancellationTokenSource _cts;

    private Timer? _timer = null;

    public TimerService(IQueueService queueService, StatsService statsService)
    {
        _queueService = queueService;
        _statsService = statsService;

        _cts = new CancellationTokenSource();
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _timer = new Timer(SendStats, null, TimeSpan.Zero, TimeSpan.FromSeconds(15));

        return Task.CompletedTask;
    }

    private void SendStats(object? state)
    {
        var stats = _queueService.GetStats();

        Task.WaitAll(_statsService.SendStats(stats, _cts.Token));
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _cts.Cancel();
        
        _timer?.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }
}

Register this as a hosted service and .NET will take care of running it for you.

builder.Services.AddHostedService<TimerService>();

The timer within the service calls the StatsService that calls the clients with the data. This does your heavy lifting of loading the data and then calling the hub to send it out. The IHubContext<T> passed in the constructor is a SignalR interface that allows classes outside of a hub to send messages. More info here

public class StatsService
{
    private readonly IHubContext<StatsHub> _hub;

    public StatsService(IHubContext<StatsHub> hub)
    {
        _hub = hub;
    }

    public async Task SendStats(int[] stats, CancellationToken cancellationToken)
    {
        await _hub.Clients.All.SendAsync("SendQueueStats", stats, cancellationToken);
    }
}

Unless you want to do something specific when the client connects or disconnects to the hub, it can be simplified down to:

[Authorize]
public class StatsHub : Hub
{
}

Information about using IHostedService to run a timed job can be found on Microsoft Learn

huangapple
  • 本文由 发表于 2023年4月7日 01:10:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/75952089.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定