问题描述
我已经在Api控制器中声明了System.Timers.Timer。
接下来,有一个JavaScript客户端调用了一个Action,它的任务是每秒向外部服务器发送HTTP GET请求,该服务器将发送回JSON。
然后,JSON通过WebSocket发送到Javascript客户端。
[Route("api")]
[ApiController]
public class PositionController : ControllerBase
{
private System.Timers.Timer aTimer = new System.Timers.Timer();
// ...
// GET api/position/state
[HttpGet("[controller]/[action]")]
public async Task<string> StateAsync()
{
try
{
Console.WriteLine("In StateAsync (GET)");
string json = "timer started";
aTimer.Elapsed += new ElapsedEventHandler(async (sender,args) =>
{
json = await Networking.SendGetRequestAsync("www.example.com");
Console.WriteLine($"Json in response:");
Console.WriteLine(json);
await _hubContext.Clients.All.SendAsync("ReceiveMessage",json);
});
aTimer.Interval = 1000;
aTimer.Enabled = true;
Console.WriteLine("----------------------------------");
return json;
}
catch (HttpRequestException error) // Connection problems
{
// ...
}
}
// GET api/position/stopstate
[HttpGet("[controller]/[action]")]
public async Task<string> StopStateAsync()
{
try
{
Console.WriteLine("In StopStateAsync (GET)");
string json = "timer stopped";
aTimer.Enabled = false;
Console.WriteLine("----------------------------------");
return json;
}
catch (HttpRequestException error) // Connection problems
{
// ...
}
}
// ...
}
问题是,由于ASP.NET控制器(所以是.Net Core控制器?)gets instancieted for every new request,当我调用Stop计时器方法时,计时器不会停止,因为它不是正确的Timer实例。因此系统继续发出HTTP请求和Websocket传输...
是否有一种方法可以保存和处理Timer实例,而我需要从其他Controller实例中停止,还是可以检索原始Controller实例?
在此先感谢大家:)
解决方法
您应该真正让控制器执行“控制器”操作。在控制器中运行计时器会破坏控制器的模式。
您应该考虑实现一个在注入will maintain a timer时使用的IHostedService
。
这是一个简单的例子:
TimerController.cs
[ApiController,Route("api/[controller]")]
public sealed class TimerController : ControllerBase
{
private readonly ITimedHostedService _timedHostedService;
public TimerController(ITimedHostedService timedHostedService)
{
_timedHostedService = timedHostedService;
}
// Just a tip: Use HttpPost. HttpGet should never change the
// state of your application. You can accidentally hit a GET,// while POST takes a little more finesse to execute.
[HttpPost,Route("startTimer/{milliseconds}")]
public IActionResult StartTimer(int milliseconds)
{
_timedHostedService.StartTimer(milliseconds);
return Ok();
}
[HttpPost,Route("stopTimer")]
public IActionResult StopTimer()
{
_timedHostedService.StopTimer();
return Ok();
}
[HttpGet,Route("isTimerRunning")]
public IActionResult IsTimerRunning()
{
return Ok(new
{
result = _timedHostedService.IsTimerRunning()
});
}
}
TimedHostedService.cs
public interface ITimedHostedService
{
void StartTimer(int milliseconds);
void StopTimer();
bool IsTimerRunning();
}
public sealed class TimedHostedService : IHostedService,ITimedHostedService
{
private static Timer _timer;
private static readonly object _timerLock = new object();
public void StartTimer(int milliseconds)
{
lock(_timerLock)
{
_timer ??= new Timer(_ =>
{
// TODO: do your timed work here.
},null,milliseconds);
}
}
public bool IsTimerRunning()
{
lock(_timerLock)
{
return _timer != null;
}
}
public void StopTimer()
{
lock(_timerLock)
{
_timer?.Change(Timeout.Infinite,Timeout.Infinite);
_timer?.Dispose();
_timer = null;
}
}
public Task StartAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
StopTimer();
return Task.CompletedTask;
}
}
然后按如下所示注入它:
services.AddHostedService<TimedHostedService>();
services.AddTransient<ITimedHostedService,TimedHostedService>();
我还没有测试过,但是它应该可以正常工作。