Last active
March 23, 2020 18:35
-
-
Save Kittoes0124/503143285c3bb457c9d4b08fa33922f3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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