Skip to content

Instantly share code, notes, and snippets.

@Kittoes0124
Last active March 23, 2020 18:35
Show Gist options
  • Save Kittoes0124/503143285c3bb457c9d4b08fa33922f3 to your computer and use it in GitHub Desktop.
Save Kittoes0124/503143285c3bb457c9d4b08fa33922f3 to your computer and use it in GitHub Desktop.
/*
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host
https://docs.microsoft.com/en-us/azure/azure-app-configuration/
https://docs.microsoft.com/en-us/dotnet/architecture/microservices/multi-container-microservice-net-applications/background-tasks-with-ihostedservice
*/
using Azure.Identity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.AzureAppConfiguration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.ApplicationInsights;
using Microsoft.Extensions.Options;
using Microsoft.FeatureManagement;
using Microsoft.FeatureManagement.FeatureFilters;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace ByteTerrace.Samples.ConsoleWorker
{
public sealed class Program
{
public static async Task Main(string[] args) {
using (var applicationHost = InitializeHost(args))
using (var cancellationTokenSource = new CancellationTokenSource()) {
await applicationHost.StartAsync(cancellationTokenSource.Token);
await applicationHost.WaitForShutdownAsync(cancellationTokenSource.Token);
}
}
private static IHost InitializeHost(string[] args) {
var configurationRefresher = ((IConfigurationRefresher)null);
return Host
.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostBuilderContext, configurationBuilder) => {
var initialConfiguration = configurationBuilder.Build();
configurationBuilder.AddAzureAppConfiguration(
action: appConfigurationOptions => {
var appConfigurationConnectionString = initialConfiguration.GetConnectionString("ApplicationConfiguration");
var appConfigurationEndpoint = initialConfiguration.GetValue<string>("ApplicationConfiguration:Endpoint");
var appConfigurationIsEnabled = false;
var defaultAzureCredential = new DefaultAzureCredential(includeInteractiveCredentials: true);
if (!string.IsNullOrEmpty(appConfigurationEndpoint)) {
appConfigurationOptions.Connect(new Uri(appConfigurationEndpoint), defaultAzureCredential);
appConfigurationIsEnabled = true;
}
else if (!string.IsNullOrEmpty(appConfigurationConnectionString)) {
appConfigurationOptions.Connect(appConfigurationConnectionString);
appConfigurationIsEnabled = true;
}
if (appConfigurationIsEnabled) {
appConfigurationOptions
.Select(KeyFilter.Any, LabelFilter.Null)
.ConfigureKeyVault(configurationKeyVaultOptions => {
configurationKeyVaultOptions.SetCredential(defaultAzureCredential);
})
.ConfigureRefresh(configurationRefreshOptions => {
var appConfigurationRefreshKeyName = initialConfiguration["ApplicationConfiguration:RefreshKeyName"];
if (!string.IsNullOrEmpty(appConfigurationRefreshKeyName)) {
configurationRefreshOptions.Register(key: appConfigurationRefreshKeyName, label: LabelFilter.Null, refreshAll: true);
}
configurationRefreshOptions.SetCacheExpiration(TimeSpan.FromMinutes(3));
})
.UseFeatureFlags(featureFlagOptions => {
featureFlagOptions.CacheExpirationTime = TimeSpan.FromMinutes(3);
});
configurationRefresher = appConfigurationOptions.GetRefresher();
}
},
optional: false
);
})
.ConfigureLogging((hostBuilderContext, loggingBuilder) => {
loggingBuilder.AddFilter<ApplicationInsightsLoggerProvider>(string.Empty, LogLevel.Trace);
})
.ConfigureServices((hostBuilderContext, serviceCollection) => {
if (null != configurationRefresher) {
serviceCollection.AddSingleton(configurationRefresher);
}
serviceCollection.AddOptions();
serviceCollection.Configure<WorkerSettings>(hostBuilderContext.Configuration);
serviceCollection
.AddFeatureManagement()
.AddFeatureFilter<PercentageFilter>()
.AddFeatureFilter<TimeWindowFilter>();
serviceCollection.AddHostedService<Worker>();
serviceCollection.AddApplicationInsightsTelemetryWorkerService();
})
.Build();
}
}
public abstract class WorkerBase : BackgroundService
{
private static Func<IServiceScope, CancellationToken, Task> DefaultOnWorkerExecute { get; } = async (serviceScope, cancellationToken) => await Task.Delay(TimeSpan.FromSeconds(3), cancellationToken);
public IConfigurationRefresher? ConfigurationRefresher { get; }
public IHostApplicationLifetime HostApplicationLifetime { get; }
public ILogger<WorkerBase> Logger { get; }
public IServiceScopeFactory ServiceScopeFactory { get; }
public WorkerSettings? WorkerSettings { get; }
protected WorkerBase(IConfigurationRefresher configurationRefresher, IHostApplicationLifetime hostApplicationLifetime, ILogger<WorkerBase> logger, IServiceScopeFactory serviceScopeFactory, IOptionsMonitor<WorkerSettings> workerSettings) {
ConfigurationRefresher = configurationRefresher;
HostApplicationLifetime = hostApplicationLifetime;
Logger = logger;
ServiceScopeFactory = serviceScopeFactory;
WorkerSettings = workerSettings.CurrentValue;
}
protected override async Task ExecuteAsync(CancellationToken cancellationToken) {
var loopIndex = 0UL;
var onWorkerExecute = WorkerSettings.OnWorkerExecute;
var onWorkerStarted = WorkerSettings.OnWorkerStarted;
var onWorkerStopped = WorkerSettings.OnWorkerStopped;
var onWorkerStopping = WorkerSettings.OnWorkerStopping;
var serviceScope = ((IServiceScope)null);
if (null == onWorkerExecute) { onWorkerExecute = DefaultOnWorkerExecute; }
if (null != onWorkerStarted) { HostApplicationLifetime.ApplicationStarted.Register(onWorkerStarted); }
if (null != onWorkerStopped) { HostApplicationLifetime.ApplicationStopped.Register(onWorkerStopped); }
if (null != onWorkerStopping) { HostApplicationLifetime.ApplicationStopping.Register(onWorkerStopping); }
while (!cancellationToken.IsCancellationRequested) {
await ConfigurationRefresher.RefreshAsync();
try {
serviceScope = ServiceScopeFactory.CreateScope();
await onWorkerExecute(serviceScope, cancellationToken);
Logger.LogTrace($"Worker loop execution; iteration {loopIndex++}.");
}
finally {
if (null != serviceScope) {
if (serviceScope is IAsyncDisposable asyncDisposable) {
await asyncDisposable.DisposeAsync();
}
else {
serviceScope.Dispose();
}
}
}
}
}
}
public sealed class Worker : WorkerBase
{
public Worker(IConfigurationRefresher configurationRefresher, IHostApplicationLifetime hostApplicationLifetime, ILogger<WorkerBase> logger, IServiceScopeFactory serviceScopeFactory, IOptionsMonitor<WorkerSettings> workerSettings) :
base(configurationRefresher, hostApplicationLifetime, logger, serviceScopeFactory, workerSettings) { }
}
public sealed class WorkerSettings
{
public Func<IServiceScope, CancellationToken, Task>? OnWorkerExecute { get; set; }
public Action? OnWorkerStarted { get; set; }
public Action? OnWorkerStopped { get; set; }
public Action? OnWorkerStopping { get; set; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment