Repetitive tasks in the .NET Core Console application

Short version - how do I (in 2017, when .NET Core 1.1 was released) run repetitive tasks in a .NET Core console application with Microsoft dependency injection ?

Long story and code snippets.

goal

I have 4 (and there will be more) services, each of which will say one method that performs a specific isolated task and interacts with the database. In particular, I have one service that deletes old data from the database, one service that "caches" the data (runs the data in the database and stores the aggregated results in another table), which generates random data and so on ... The idea consists in completing these tasks at different intervals, say, every 20 seconds or once a day.

Current dirty implementation

I have a service that for each task has a while(true) that starts the service using DI, starts the task, Thread.Sleep(interval) , checks the exit condition (class flag) and continue/break . Most methods: async .

Current issues

First of all, I know that while(true) and Thread.Sleep(interval) are a huge hack (technical duty). Secondly, I have a memory leak, and I feel that the way I start / manage it has something to do with it. Finally, the code encounters random errors, and all exceptions are swallowed. I can’t catch them in any way I know.

The solution I want

I want to achieve the goal, but if possible

  • Avoid large frames like Hangfire and Quartz. I feel like I don’t need part of what they can.
  • save the application as a console application. Although this is an "assistant" for an ASP project, I want it to be a console application and be able to run it as dotnet daemons.dll , and it does its work indefinitely until Ctrl+C . It also should not grow in memory and CPU / thread consumption. This is normal if it consumes a significant amount of RAM, if it is a stable number.
  • use Microsoft DI. This is how all my services are built.
  • save my async services. I can reuse them in different scenarios, including unit testing.

Code snippets

An example of how I run a repeating task:

 private async Task RunCacheServiceAsync() { // Make sure the service is set to run. _status[ServiceManagerServices.Cache] = true; _logger.LogInformation(LoggingEvents.ServiceManager.AsInt(), "Cache service started."); while (true) { var metrics = await _serviceProvider .GetRequiredService<DataContext>() .Metrics .ToListAsync(); // Run the tasks var tasks = metrics .Select( (metric) => Task.Factory.StartNew(async () => { await _serviceProvider .GetRequiredService<ICacheService>() .CacheMetric(metric); }) ); // Wait completion of all tasks await Task.WhenAll(tasks.ToArray()); // Wait Thread.Sleep(_intervals[ServiceManagerServices.Cache]); // Check exit condition if (!_status[ServiceManagerServices.Cache]) { break; } } } 

An example of a task that should be periodic:

 public async Task CleanDataPointsAsync() { var toTimestamp = DateTime.Now - _maxAge; // remove data points of all types _context.RemoveRange( _context.NumericDataPoints.Where(dp => dp.Timestamp < toTimestamp) ); await _context.SaveChangesAsync(); _logger.LogInformation(LoggingEvents.Clean.AsInt(), "Cleaned old data."); } 

Question

Please suggest a general approach that I can take to achieve the goal, taking into account the limitations. Any feedback on the posted code and reasoning is appreciated!

+5
source share

Source: https://habr.com/ru/post/1266279/


All Articles