ASP.Net Core IHosted background service внезапно выключается
Ищу помощи, уже всю голову сломал. В общем есть приложение ASP.NET Core Razor Pages (.Net 5), хостится на IIS. У приложения есть фоновый сервис, который по расписанию запускает несколько методов. При выполнении приложения происходит 2 проблемы:
- Фоновый сервис прекращает работу через 20 минут (IIS настраивал на бесперебойную работу).
- Фоновый сервис при выполнении определенных действий выключается.
При этом, если приложение запустить из командной строки, то все работает прекрасно и без сбоев. Пробовал, оборачивать все в try catch для обработки исключений, но их нет. При поиске проблем обнаружил, что если в методе CheckChanges в цикле foreach (var ca in result.УдостоверяющийЦентр.Where(x=>x.РеестровыйНомер != 0)) закоментить исполянемый код, то сервис не падает. P.S. Проблема только в IIS.
В логах приложения:
info: CronJobUpdateCA[0]
08:50:00 CronJob is working.
info: Microsoft.Hosting.Lifetime[0]
Application is shutting down...
Вот код сервиса:
BackgroundService.cs
public abstract class BackgroundService : IHostedService
{
private Task _executingTask;
private readonly CancellationTokenSource _stoppingCts =
new CancellationTokenSource();
public virtual Task StartAsync(CancellationToken cancellationToken)
{
_executingTask = ExecuteAsync(_stoppingCts.Token);
if (_executingTask.IsCompleted)
{
return _executingTask;
}
// Otherwise it's running
return Task.CompletedTask;
}
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
// Stop called without start
if (_executingTask == null)
{
return;
}
try
{
_stoppingCts.Cancel();
}
finally
{
await Task.WhenAny(_executingTask, Task.Delay(5000,
cancellationToken));
}
}
protected virtual async Task ExecuteAsync(CancellationToken stoppingToken)
{
do
{
await Process();
await Task.Delay(30000, stoppingToken); //5 seconds delay
}
while (!stoppingToken.IsCancellationRequested);
}
protected abstract Task Process();
}
ScopedProcessor.cs
public abstract class ScopedProcessor : BackgroundService
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public ScopedProcessor(IServiceScopeFactory serviceScopeFactory) : base()
{
_serviceScopeFactory = serviceScopeFactory;
}
protected override async Task Process()
{
using (var scope = _serviceScopeFactory.CreateScope())
{
await ProcessInScope(scope.ServiceProvider);
}
}
public abstract Task ProcessInScope(IServiceProvider serviceProvider);
}
SchedulerProcessor.cs
public abstract class ScheduledProcessor : ScopedProcessor
{ private CrontabSchedule _schedule; private DateTime _nextRun;
protected abstract string Schedule { get; }
public ScheduledProcessor(IServiceScopeFactory serviceScopeFactory) : base(serviceScopeFactory)
{
_schedule = CrontabSchedule.Parse(Schedule);
_nextRun = _schedule.GetNextOccurrence(DateTime.Now);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
do
{
var now = DateTime.Now;
if (now > _nextRun)
{
await Process();
_nextRun = _schedule.GetNextOccurrence(DateTime.Now);
}
await Task.Delay(5000, stoppingToken); //5 seconds delay
}
while (!stoppingToken.IsCancellationRequested);
}
}
SchedulerTask.cs
public class ScheduleTask : ScheduledProcessor
{
private readonly IConfiguration _configuration;
public ScheduleTask(IServiceScopeFactory serviceScopeFactory, IConfiguration configuration) : base(serviceScopeFactory)
{
_configuration = configuration;
}
protected override string Schedule => "*/2 * * * *";
public override Task ProcessInScope(IServiceProvider serviceProvider)
{
var updater = new CAUpdater(_configuration);
updater.Test();
return Task.CompletedTask;
}
метод Test
public void Test()
{
ReadFile(); //Десериализация xml файла.
CheckChanges();
}
метод CheckChanges()
private void CheckChanges()
{
try
{
var context = new ApplicationContext();
Update upd = new Update();
upd.Date = result?.Дата ?? DateTime.Now;
upd.Version = result?.Версия ?? 0;
upd.FileName = fileName;
upd.Error = _errorBody.ToString();
update = context.Updates.Add(upd).Entity;
if (result == null)
{
context.SaveChanges();
return;
}
foreach (var ca in context.CA.Where(x => !x.Deleted).ToList())
{
if (result.УдостоверяющийЦентр.All(x => x.РеестровыйНомер != 0 ? x.РеестровыйНомер != ca.Id : x.КраткоеНазвание != ca.Name))
{
ca.Deleted = true;
context.CA.Update(ca);
context.СaChanges.Add(new СaChange()
{
Update = update,
CA = ca,
Action = "Delete",
});
}
}
foreach (var ca in result.УдостоверяющийЦентр.Where(x=>x.РеестровыйНомер != 0))
{
var item =
ca.РеестровыйНомер != 0
? context.CA.FirstOrDefault(x => x.Id == ca.РеестровыйНомер)
: context.CA.FirstOrDefault(x => x.Name == ca.КраткоеНазвание);
// дальше идет код......
}
}