Skip to content

Instantly share code, notes, and snippets.

@tuespetre
Last active June 27, 2016 16:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tuespetre/29369fcffc8a825fdd872923459f035e to your computer and use it in GitHub Desktop.
Save tuespetre/29369fcffc8a825fdd872923459f035e to your computer and use it in GitHub Desktop.
Extensions for background tasks inside of a .NET core web app
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Builder
{
public static class WebApplicationBackgroundTaskExtensions
{
/// <summary>
/// https://gist.github.com/tuespetre/29369fcffc8a825fdd872923459f035e
/// </summary>
public static void Use(this IApplicationBuilder app, LongRunningBackgroundTask backgroundTask)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
var services = app.ApplicationServices;
var lifetime = services.GetRequiredService<IApplicationLifetime>();
var scopeFactory = services.GetRequiredService<IServiceScopeFactory>();
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger<LongRunningBackgroundTask>();
lifetime.ApplicationStarted.Register(async () =>
{
while (!lifetime.ApplicationStopping.IsCancellationRequested)
{
using (logger.BeginScope("{TaskName}", backgroundTask.Name ?? "Unnamed long-running task"))
{
try
{
logger.LogDebug("Beginning execution");
using (var scope = scopeFactory.CreateScope())
{
await backgroundTask.Delegate(scope.ServiceProvider, lifetime.ApplicationStopping);
}
logger.LogDebug("Completed execution");
}
catch (OperationCanceledException exception)
{
logger.LogDebug(0, exception, "Caught OperationCanceledException");
}
catch (Exception exception)
{
logger.LogError(0, exception, "Uncaught Exception");
break;
}
}
}
});
}
/// <summary>
/// https://gist.github.com/tuespetre/29369fcffc8a825fdd872923459f035e
/// </summary>
public static void Use(this IApplicationBuilder app, RecurringBackgroundTask backgroundTask)
{
if (app == null)
{
throw new ArgumentNullException(nameof(app));
}
var services = app.ApplicationServices;
var lifetime = services.GetRequiredService<IApplicationLifetime>();
var scopeFactory = services.GetRequiredService<IServiceScopeFactory>();
var loggerFactory = services.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger<RecurringBackgroundTask>();
var entered = 0;
Timer timer = null;
TimerCallback callback = async self =>
{
if (Interlocked.Increment(ref entered) == 1)
{
try
{
using (logger.BeginScope("{TaskName}", backgroundTask.Name ?? "Unnamed recurring task"))
{
try
{
logger.LogDebug("Beginning execution");
using (var scope = scopeFactory.CreateScope())
{
await backgroundTask.Delegate(scope.ServiceProvider, lifetime.ApplicationStopping);
}
logger.LogDebug("Completed execution");
}
catch (OperationCanceledException exception)
{
logger.LogDebug(0, exception, "Caught OperationCanceledException");
}
catch (Exception exception)
{
logger.LogError(0, exception, "Uncaught Exception");
timer?.Dispose();
timer = null;
}
}
}
finally
{
Interlocked.Exchange(ref entered, 0);
}
}
};
lifetime.ApplicationStarted.Register(() =>
{
timer = new Timer(callback, callback, TimeUntilNext(backgroundTask.Interval), backgroundTask.Interval);
if (backgroundTask.RunImmediately)
{
callback(callback);
}
lifetime.ApplicationStopping.Register(() => timer?.Dispose());
});
}
private static TimeSpan TimeUntilNext(TimeSpan interval)
{
return new DateTime(((DateTime.Now.Ticks + interval.Ticks - 1) / interval.Ticks) * interval.Ticks) - DateTime.Now;
}
}
public struct LongRunningBackgroundTask
{
public string Name { get; set; }
public Func<IServiceProvider, CancellationToken, Task> Delegate { get; set; }
}
public struct RecurringBackgroundTask
{
public string Name { get; set; }
public bool RunImmediately { get; set; }
public TimeSpan Interval { get; set; }
public Func<IServiceProvider, CancellationToken, Task> Delegate { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment