Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/327.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在ASP.NET Core 2.1中的计时器上运行BackgroundService_C#_Asp.net Core_Background Process_Asp.net Core 2.1_Background Service - Fatal编程技术网

C# 如何在ASP.NET Core 2.1中的计时器上运行BackgroundService

C# 如何在ASP.NET Core 2.1中的计时器上运行BackgroundService,c#,asp.net-core,background-process,asp.net-core-2.1,background-service,C#,Asp.net Core,Background Process,Asp.net Core 2.1,Background Service,我想在ASP.NET Core 2.1中运行后台作业。它必须每2小时运行一次,并且需要访问我的DI容器,因为它将在数据库中执行一些清理。它需要是异步的,并且应该独立于我的ASP.NET Core 2.1应用程序运行 我看到有一个IHostedService,但是ASP.NET Core 2.1还引入了一个名为BackgroundService的抽象类,它为我做了更多的工作。看起来不错,我想用它 不过,我还没有弄清楚如何在计时器上运行从BackgroundService派生的服务 我是否需要在Ex

我想在ASP.NET Core 2.1中运行后台作业。它必须每2小时运行一次,并且需要访问我的DI容器,因为它将在数据库中执行一些清理。它需要是异步的,并且应该独立于我的ASP.NET Core 2.1应用程序运行

我看到有一个
IHostedService
,但是ASP.NET Core 2.1还引入了一个名为
BackgroundService
的抽象类,它为我做了更多的工作。看起来不错,我想用它

不过,我还没有弄清楚如何在计时器上运行从
BackgroundService
派生的服务

我是否需要在
ExecuteAsync(token)
中配置它,方法是记住它上次运行的时间并确定这是否是2小时,还是有更好/更干净的方法只说它必须每2小时运行一次

另外,我使用
后台服务解决问题的方法正确吗

谢谢大家!

编辑:


将此发布在

上实现这一点的一种方法是使用HangFire.io,它将处理预定的后台任务,管理服务器间的平衡,并且具有相当的可扩展性


请参见更新于2020年4月的《重复性工作》,请在底部阅读

@Panagiotis Kanavos在对我的问题的评论中给出了答案,但没有将其作为实际答案发布;这个答案是献给他/她的

我使用了一个类似于MicrosoftDocs的工具来创建该服务

internal class TimedHostedService : IHostedService, IDisposable
{
    private readonly ILogger _logger;
    private Timer _timer;

    public TimedHostedService(ILogger<TimedHostedService> logger)
    {
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Timed Background Service is starting.");

        _timer = new Timer(DoWork, null, TimeSpan.Zero, 
            TimeSpan.FromSeconds(5));

        return Task.CompletedTask;
    }

    private void DoWork(object state)
    {
        _logger.LogInformation("Timed Background Service is working.");
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Timed Background Service is stopping.");

        _timer?.Change(Timeout.Infinite, 0);

        return Task.CompletedTask;
    }

    public void Dispose()
    {
        _timer?.Dispose();
    }
}
内部类TimedHostedService:IHostedService,IDisposable
{
专用只读ILogger\u记录器;
私人定时器;
公共TimedHostedService(ILogger记录器)
{
_记录器=记录器;
}
公共任务StartSync(CancellationToken CancellationToken)
{
_logger.LogInformation(“定时后台服务正在启动”);
_计时器=新计时器(DoWork、null、TimeSpan.Zero、,
时间跨度从秒(5)开始;
返回Task.CompletedTask;
}
私有无效工作(对象状态)
{
_logger.LogInformation(“定时后台服务正在工作。”);
}
公共任务StopAsync(CancellationToken CancellationToken)
{
_logger.LogInformation(“定时后台服务正在停止”);
_计时器?更改(Timeout.Infinite,0);
返回Task.CompletedTask;
}
公共空间处置()
{
_计时器?.Dispose();
}
}
在我的例子中,我通过执行
newtimer(async()=>wait-doworksync(),…)
使
\u定时器调用async

将来,可以编写一个扩展,使这样的类在Extensions repo中可用,因为我认为这非常有用。我在描述中发布了github问题链接

一个提示,如果您打算重用多个托管服务的这个类,考虑创建一个基类,该基类包含计时器和抽象<代码> PrimWorkWord()/<代码>或某物,因此“时间”逻辑只在一个地方。 谢谢你的回答!我希望这对将来的人有所帮助

更新04-2020

使用普通的核心服务集合DI容器(开箱即用),在这里注入作用域服务是不可能的。我使用的是autofac,由于注册错误,它可以在构造函数中使用作用域服务,如
IClassRepository
,但当我开始处理一个仅使用
AddScoped()、AddSingleton()、AddTransient()的不同项目时
我们发现注入作用域的东西不起作用,因为您不在作用域上下文中


为了使用您的作用域服务,请插入一个
IServiceScopeFactory
(更易于测试)并使用
CreateScope()
,它允许您使用
scope.GetService()
using
语句:)

基于以前的响应和

改进:

  • 在上一个任务完成执行之前,它不会启动计时器。这将有助于避免陷入两个任务同时执行的情况
  • 它支持异步任务
  • 它在任务执行期间处理可能的异常,以确保不会阻止下一个任务的执行
  • 对于执行的每个任务,都会创建一个作用域,因此您可以访问RunJobAsync中的任何作用域服务
  • 可以在继承的类中指定间隔和初始任务执行时间
  • 访问作用域服务示例

        protected override async Task RunJobAsync(IServiceProvider serviceProvider, CancellationToken stoppingToken)
        {
                DbContext context = serviceProvider.GetRequiredService<DbContext>();
        }
    
    受保护的覆盖异步任务RunJobAsync(IServiceProvider服务提供程序,CancellationToken stoppingToken)
    {
    DbContext上下文=serviceProvider.GetRequiredService();
    }
    
    源代码:

    public abstract class TimedHostedService : IHostedService, IDisposable
    {
        private readonly ILogger _logger;
        private Timer _timer;
        private Task _executingTask;
        private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();
    
        IServiceProvider _services;
        public TimedHostedService(IServiceProvider services)
        {
            _services = services;
            _logger = _services.GetRequiredService<ILogger<TimedHostedService>>();
            
        }
    
        public Task StartAsync(CancellationToken cancellationToken)
        {
            _timer = new Timer(ExecuteTask, null,FirstRunAfter, TimeSpan.FromMilliseconds(-1));
    
            return Task.CompletedTask;
        }
    
        private void ExecuteTask(object state)
        {
            _timer?.Change(Timeout.Infinite, 0);
            _executingTask = ExecuteTaskAsync(_stoppingCts.Token);
        }
    
        private async Task ExecuteTaskAsync(CancellationToken stoppingToken)
        {
            try
            {
                using (var scope = _services.CreateScope())
                {
                    await RunJobAsync(scope.ServiceProvider, stoppingToken);
                }
            }
            catch (Exception exception)
            {
                _logger.LogError("BackgroundTask Failed", exception);
            }
            _timer.Change(Interval, TimeSpan.FromMilliseconds(-1));
        }
    
        /// <summary>
        /// This method is called when the <see cref="IHostedService"/> starts. The implementation should return a task 
        /// </summary>
        /// <param name="serviceProvider"></param>
        /// <param name="stoppingToken">Triggered when <see cref="IHostedService.StopAsync(CancellationToken)"/> is called.</param>
        /// <returns>A <see cref="Task"/> that represents the long running operations.</returns>
        protected abstract Task RunJobAsync(IServiceProvider serviceProvider, CancellationToken stoppingToken);
        protected abstract TimeSpan Interval { get; }
        
        protected abstract TimeSpan FirstRunAfter { get; }
        
        public virtual async Task StopAsync(CancellationToken cancellationToken)
        {
            _timer?.Change(Timeout.Infinite, 0);
    
            // Stop called without start
            if (_executingTask == null)
            {
                return;
            }
    
            try
            {
                // Signal cancellation to the executing method
                _stoppingCts.Cancel();
            }
            finally
            {
                // Wait until the task completes or the stop token triggers
                await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
            }
    
        }
    
        public void Dispose()
        {
            _stoppingCts.Cancel();
            _timer?.Dispose();
        }
    }
    
    公共抽象类TimedHostedService:IHostedService,IDisposable
    {
    专用只读ILogger\u记录器;
    私人定时器;
    私有任务(executingTask);;
    私有只读CancellationTokenSource _stoppingCts=new CancellationTokenSource();
    IServiceProvider服务;
    公共时间后台服务(IServiceProvider服务)
    {
    _服务=服务;
    _记录器=_services.GetRequiredService();
    }
    公共任务StartSync(CancellationToken CancellationToken)
    {
    _计时器=新计时器(ExecuteTask,null,FirstRunAfter,TimeSpan.FromMissels(-1));
    返回Task.CompletedTask;
    }
    私有void ExecuteTask(对象状态)
    {
    _计时器?更改(Timeout.Infinite,0);
    _executingTask=ExecuteTaskAsync(_stoppingCts.Token);
    }
    专用异步任务ExecuteTaskAsync(CancellationToken stoppingToken)
    {
    尝试
    {
    使用(var scope=\u services.CreateScope())
    {
    等待RunJobAsync(scope.ServiceProvider,stoppingToken);
    }
    }
    捕获(异常)
    {
    _logger.LogError(“BackgroundTask失败”,异常);
    }
    _定时器