public void Should_call_onError_delegate_if_cache_put_errors() { Exception ex = new Exception(); ISyncCacheProvider stubCacheProvider = new StubErroringCacheProvider(getException: null, putException: ex); Exception exceptionFromCacheProvider = null; const string valueToReturn = "valueToReturn"; const string executionKey = "SomeExecutionKey"; Action <Context, string, Exception> onError = (ctx, key, exc) => { exceptionFromCacheProvider = exc; }; CachePolicy cache = Policy.Cache(stubCacheProvider, TimeSpan.MaxValue, onError); stubCacheProvider.Get(executionKey).Should().BeNull(); cache.Execute(() => { return(valueToReturn); }, new Context(executionKey)).Should().Be(valueToReturn); // error should be captured by onError delegate. exceptionFromCacheProvider.Should().Be(ex); // failed to put it in the cache stubCacheProvider.Get(executionKey).Should().BeNull(); }
public void Should_return_value_from_cache_and_not_execute_delegate_if_cache_holds_value_when_mid_policywrap() { const string valueToReturnFromCache = "valueToReturnFromCache"; const string valueToReturnFromExecution = "valueToReturnFromExecution"; const string executionKey = "SomeExecutionKey"; ISyncCacheProvider stubCacheProvider = new StubCacheProvider(); CachePolicy <string> cache = Policy.Cache <string>(stubCacheProvider, TimeSpan.MaxValue); Policy <string> noop = Policy.NoOp <string>(); PolicyWrap <string> wrap = Policy.Wrap(noop, cache, noop); stubCacheProvider.Put(executionKey, valueToReturnFromCache, new Ttl(TimeSpan.MaxValue)); bool delegateExecuted = false; wrap.Execute(() => { delegateExecuted = true; return(valueToReturnFromExecution); }, new Context(executionKey)) .Should().Be(valueToReturnFromCache); delegateExecuted.Should().BeFalse(); }
public void Should_execute_oncachemiss_and_oncacheput_if_cache_does_not_hold_value_and_put() { const string valueToReturn = "valueToReturn"; const string operationKey = "SomeOperationKey"; string keyPassedToOnCacheMiss = null; string keyPassedToOnCachePut = null; Context contextToExecute = new Context(operationKey); Context contextPassedToOnCacheMiss = null; Context contextPassedToOnCachePut = null; Action <Context, string, Exception> noErrorHandling = (_, __, ___) => { }; Action <Context, string> emptyDelegate = (_, __) => { }; Action <Context, string> onCacheMiss = (ctx, key) => { contextPassedToOnCacheMiss = ctx; keyPassedToOnCacheMiss = key; }; Action <Context, string> onCachePut = (ctx, key) => { contextPassedToOnCachePut = ctx; keyPassedToOnCachePut = key; }; ISyncCacheProvider stubCacheProvider = new StubCacheProvider(); CachePolicy cache = Policy.Cache(stubCacheProvider, new RelativeTtl(TimeSpan.MaxValue), DefaultCacheKeyStrategy.Instance, emptyDelegate, onCacheMiss, onCachePut, noErrorHandling, noErrorHandling); (bool cacheHit1, object fromCache1) = stubCacheProvider.TryGet(operationKey); cacheHit1.Should().BeFalse(); fromCache1.Should().BeNull(); cache.Execute(ctx => valueToReturn, contextToExecute).Should().Be(valueToReturn); (bool cacheHit2, object fromCache2) = stubCacheProvider.TryGet(operationKey); cacheHit2.Should().BeTrue(); fromCache2.Should().Be(valueToReturn); contextPassedToOnCacheMiss.Should().BeSameAs(contextToExecute); keyPassedToOnCacheMiss.Should().Be(operationKey); contextPassedToOnCachePut.Should().BeSameAs(contextToExecute); keyPassedToOnCachePut.Should().Be(operationKey); }
public void Should_honour_cancellation_during_delegate_execution_and_not_put_to_cache() { const string valueToReturn = "valueToReturn"; const string operationKey = "SomeOperationKey"; ISyncCacheProvider stubCacheProvider = new StubCacheProvider(); CachePolicy cache = Policy.Cache(stubCacheProvider, TimeSpan.MaxValue); CancellationTokenSource tokenSource = new CancellationTokenSource(); Func <Context, CancellationToken, string> func = (ctx, ct) => { tokenSource.Cancel(); // simulate cancellation raised during delegate execution ct.ThrowIfCancellationRequested(); return(valueToReturn); }; cache.Invoking(policy => policy.Execute(func, new Context(operationKey), tokenSource.Token)) .Should().Throw <OperationCanceledException>(); (bool cacheHit, object fromCache) = stubCacheProvider.TryGet(operationKey); cacheHit.Should().BeFalse(); fromCache.Should().BeNull(); }
public void Should_return_value_from_cache_and_not_execute_delegate_if_cache_holds_value_when_outermost_in_policywrap() { const string valueToReturnFromCache = "valueToReturnFromCache"; const string valueToReturnFromExecution = "valueToReturnFromExecution"; const string operationKey = "SomeOperationKey"; ISyncCacheProvider stubCacheProvider = new StubCacheProvider(); CachePolicy cache = Policy.Cache(stubCacheProvider, TimeSpan.MaxValue); Policy noop = Policy.NoOp(); PolicyWrap wrap = Policy.Wrap(cache, noop); stubCacheProvider.Put(operationKey, valueToReturnFromCache, new Ttl(TimeSpan.MaxValue)); bool delegateExecuted = false; wrap.Execute(ctx => { delegateExecuted = true; return(valueToReturnFromExecution); }, new Context(operationKey)) .Should().Be(valueToReturnFromCache); delegateExecuted.Should().BeFalse(); }
public void Should_return_value_from_cache_and_not_execute_delegate_if_cache_holds_value__default_for_value_type() { ResultPrimitive valueToReturnFromCache = default; ResultPrimitive valueToReturnFromExecution = ResultPrimitive.Good; valueToReturnFromExecution.Should().NotBe(valueToReturnFromCache); const string operationKey = "SomeOperationKey"; ISyncCacheProvider stubCacheProvider = new StubCacheProvider(); CachePolicy cache = Policy.Cache(stubCacheProvider, TimeSpan.MaxValue); stubCacheProvider.Put(operationKey, valueToReturnFromCache, new Ttl(TimeSpan.MaxValue)); bool delegateExecuted = false; cache.Execute(ctx => { delegateExecuted = true; return(valueToReturnFromExecution); }, new Context(operationKey)) .Should().Be(valueToReturnFromCache); delegateExecuted.Should().BeFalse(); }
public void Compose(Composition composition) { if (composition is null) { throw new ArgumentNullException(nameof(composition)); } // Utility classes composition.Register <ILogger, UmbracoLogWrapper>(); composition.Register <Email.IEmailFormatter, Email.EmailFormatter>(); composition.Register <Email.IEmailSender, Email.EmailSender>(); composition.Register <IEmailProtector, EmailProtector>(); composition.Register <IVerificationToken, VerificationToken>(); composition.Register <IAuditRepository, SqlServerAuditRepository>(); composition.Register <IRouteGenerator, RouteGenerator>(); composition.Register <IRouteNormaliser, RouteNormaliser>(); composition.Register <IApiKeyProvider, ConfigApiKeyProvider>(); composition.Register <IDateTimeFormatter, DateTimeFormatter>(); composition.Register <ISeasonEstimator, SeasonEstimator>(); composition.Register <IHtmlFormatter, Stoolball.Html.HtmlFormatter>(); composition.Register <ICreateMatchSeasonSelector, CreateMatchSeasonSelector>(); composition.Register <IMatchNameBuilder, MatchNameBuilder>(); composition.Register <IPlayerTypeSelector, PlayerTypeSelector>(); composition.Register <IEditMatchHelper, EditMatchHelper>(); composition.Register <IMatchValidator, MatchValidator>(); composition.Register <IMemberGroupHelper, MemberGroupHelper>(); composition.Register <IMatchResultEvaluator, MatchResultEvaluator>(); composition.Register <IMatchInningsUrlParser, MatchInningsUrlParser>(); composition.Register <IPlayerInningsScaffolder, PlayerInningsScaffolder>(); composition.Register <IBowlingScorecardComparer, BowlingScorecardComparer>(); composition.Register <IBattingScorecardComparer, BattingScorecardComparer>(); composition.Register <IYouTubeUrlNormaliser, YouTubeUrlNormaliser>(); composition.Register <IDataRedactor, DataRedactor>(); composition.Register <IPostSaveRedirector, PostSaveRedirector>(); composition.Register <IBowlingFiguresCalculator, BowlingFiguresCalculator>(); composition.Register <IBackgroundTaskTracker, MemoryCacheBackgroundTaskTracker>(); composition.Register <IOverSetScaffolder, OverSetScaffolder>(); composition.Register <IPlayerInMatchStatisticsBuilder, PlayerInMatchStatisticsBuilder>(); composition.Register <IPlayerIdentityFinder, PlayerIdentityFinder>(); composition.Register <IOversHelper, OversHelper>(); composition.Register <IStatisticsFilterFactory, StatisticsFilterFactory>(); composition.Register <IStatisticsBreadcrumbBuilder, StatisticsBreadcrumbBuilder>(); composition.Register <IContactDetailsParser, ContactDetailsParser>(); composition.Register <IMatchesRssQueryStringParser, MatchesRssQueryStringParser>(); composition.Register <IMatchFilterQueryStringSerializer, MatchFilterQueryStringSerializer>(); composition.Register <IStatisticsFilterQueryStringSerializer, StatisticsFilterQueryStringSerializer>(); composition.Register <ITeamListingFilterSerializer, TeamListingFilterQueryStringSerializer>(); composition.Register <IMatchLocationFilterSerializer, MatchLocationFilterQueryStringSerializer>(); composition.Register <ICompetitionFilterSerializer, CompetitionFilterQueryStringSerializer>(); composition.Register <IPlayerFilterSerializer, PlayerFilterQueryStringSerializer>(); composition.Register <ICacheOverride, CacheOverride>(); composition.Register <IBadLanguageFilter, BadLanguageFilter>(); composition.Register <IStatisticsQueryBuilder, StatisticsQueryBuilder>(); composition.Register <IStoolballEntityCopier, StoolballEntityCopier>(); composition.Register <IPlayerNameFormatter, PlayerNameFormatter>(); composition.Register <IMatchInningsFactory, MatchInningsFactory>(); composition.Register <Stoolball.Html.IHtmlSanitizer, Stoolball.Web.Html.HtmlSanitizer>(); composition.Register <Ganss.XSS.IHtmlSanitizer, Ganss.XSS.HtmlSanitizer>(); composition.Register <IUrlFormatter, UrlFormatter>(); composition.Register <ISocialMediaAccountFormatter, SocialMediaAccountFormatter>(); composition.Register <IMatchFilterQueryStringParser, MatchFilterQueryStringParser>(); composition.Register <IStatisticsFilterQueryStringParser, StatisticsFilterQueryStringParser>(); composition.Register <IMatchFilterHumanizer, MatchFilterHumanizer>(); composition.Register <IStatisticsFilterHumanizer, StatisticsFilterHumanizer>(); composition.Register <IStoolballEntityRouteParser, StoolballEntityRouteParser>(); composition.Register <IUmbracoFormsLabeller, UmbracoFormsLabeller>(); composition.Register <ICreateMemberExecuter, CreateMemberExecuter>(); composition.Register <ILoginMemberWrapper, LoginMemberWrapper>(); composition.Register <ILogoutMemberWrapper, LogoutMemberWrapper>(); // Listings pages composition.Register <IListingsModelBuilder <TeamListing, TeamListingFilter, TeamsViewModel>, ListingsModelBuilder <TeamListing, TeamListingFilter, TeamsViewModel> >(); composition.Register <IListingsModelBuilder <Competition, CompetitionFilter, CompetitionsViewModel>, ListingsModelBuilder <Competition, CompetitionFilter, CompetitionsViewModel> >(); composition.Register <IListingsModelBuilder <MatchLocation, MatchLocationFilter, MatchLocationsViewModel>, ListingsModelBuilder <MatchLocation, MatchLocationFilter, MatchLocationsViewModel> >(); composition.Register <IListingsModelBuilder <School, SchoolFilter, SchoolsViewModel>, ListingsModelBuilder <School, SchoolFilter, SchoolsViewModel> >(); // Controllers for stoolball data pages. Register the concrete class since it'll never need to // be injected anywhere except the one place where it's serving a page of content. composition.Register <IStoolballRouteTypeMapper, StoolballRouteTypeMapper>(); composition.Register <IStoolballRouterController, StoolballRouterController>(); // Caching with Polly composition.Register <IMemoryCache, MemoryCache>(Lifetime.Singleton); composition.Register <IOptions <MemoryCacheOptions>, MemoryCacheOptions>(Lifetime.Singleton); composition.Register <IAsyncCacheProvider, MemoryCacheProvider>(Lifetime.Singleton); composition.Register <ISyncCacheProvider, MemoryCacheProvider>(Lifetime.Singleton); composition.Register <IReadOnlyPolicyRegistry <string> >((serviceProvider) => { var registry = new PolicyRegistry(); var asyncMemoryCacheProvider = serviceProvider.GetInstance <IAsyncCacheProvider>(); var logger = serviceProvider.GetInstance <ILogger>(); var cachePolicy = Policy.CacheAsync(asyncMemoryCacheProvider, TimeSpan.FromMinutes(120), (context, key, ex) => { logger.Error(typeof(IAsyncCacheProvider), ex, "Cache provider for key {key}, threw exception: {ex}.", key, ex.Message); }); var syncMemoryCacheProvider = serviceProvider.GetInstance <ISyncCacheProvider>(); var slidingPolicy = Policy.Cache(syncMemoryCacheProvider, new SlidingTtl(TimeSpan.FromMinutes(120)), (context, key, ex) => { logger.Error(typeof(ISyncCacheProvider), ex, "Cache provider for key {key}, threw exception: {ex}.", key, ex.Message); }); registry.Add(CacheConstants.StatisticsPolicy, cachePolicy); registry.Add(CacheConstants.MatchesPolicy, cachePolicy); registry.Add(CacheConstants.CommentsPolicy, cachePolicy); registry.Add(CacheConstants.TeamsPolicy, cachePolicy); registry.Add(CacheConstants.CompetitionsPolicy, cachePolicy); registry.Add(CacheConstants.MatchLocationsPolicy, cachePolicy); registry.Add(CacheConstants.MemberOverridePolicy, slidingPolicy); return(registry); }, Lifetime.Singleton); composition.Register <IClearableCache, ClearableCacheWrapper>(); composition.Register <ICacheClearer <Tournament>, TournamentCacheClearer>(); composition.Register <ICacheClearer <Match>, MatchCacheClearer>(); // Data sources for stoolball data. composition.Register <IDatabaseConnectionFactory, UmbracoDatabaseConnectionFactory>(); composition.Register <IRedirectsRepository, SkybrudRedirectsRepository>(); composition.Register <IClubDataSource, SqlServerClubDataSource>(); composition.Register <IClubRepository, SqlServerClubRepository>(); composition.Register <ITeamDataSource, SqlServerTeamDataSource>(); composition.Register <ITeamListingDataSource, CachedTeamListingDataSource>(); composition.Register <ICacheableTeamListingDataSource, SqlServerTeamListingDataSource>(); composition.Register <ITeamRepository, SqlServerTeamRepository>(); composition.Register <IPlayerDataSource, CachedPlayerDataSource>(); composition.Register <ICacheablePlayerDataSource, SqlServerPlayerDataSource>(); composition.Register <IPlayerRepository, SqlServerPlayerRepository>(); composition.Register <IMatchLocationDataSource, CachedMatchLocationDataSource>(); composition.Register <ICacheableMatchLocationDataSource, SqlServerMatchLocationDataSource>(); composition.Register <IMatchLocationRepository, SqlServerMatchLocationRepository>(); composition.Register <ICompetitionDataSource, CachedCompetitionDataSource>(); composition.Register <ICacheableCompetitionDataSource, SqlServerCompetitionDataSource>(); composition.Register <ICompetitionRepository, SqlServerCompetitionRepository>(); composition.Register <ISeasonDataSource, SqlServerSeasonDataSource>(); composition.Register <ISeasonRepository, SqlServerSeasonRepository>(); composition.Register <IMatchDataSource, SqlServerMatchDataSource>(); composition.Register <IMatchListingDataSource, CachedMatchListingDataSource>(); composition.Register <ICacheableMatchListingDataSource, SqlServerMatchListingDataSource>(); composition.Register <ICommentsDataSource <Match>, CachedCommentsDataSource <Match> >(); composition.Register <ICommentsDataSource <Tournament>, CachedCommentsDataSource <Tournament> >(); composition.Register <ICacheableCommentsDataSource <Match>, SqlServerMatchCommentsDataSource>(); composition.Register <ICacheableCommentsDataSource <Tournament>, SqlServerTournamentCommentsDataSource>(); composition.Register <IMatchRepository, SqlServerMatchRepository>(); composition.Register <ITournamentDataSource, SqlServerTournamentDataSource>(); composition.Register <ITournamentRepository, SqlServerTournamentRepository>(); composition.Register <IBestPerformanceInAMatchStatisticsDataSource, CachedBestPerformanceInAMatchStatisticsDataSource>(); composition.Register <ICacheableBestPerformanceInAMatchStatisticsDataSource, SqlServerBestPerformanceInAMatchStatisticsDataSource>(); composition.Register <IStatisticsRepository, SqlServerStatisticsRepository>(); composition.Register <IInningsStatisticsDataSource, CachedInningsStatisticsDataSource>(); composition.Register <ICacheableInningsStatisticsDataSource, SqlServerInningsStatisticsDataSource>(); composition.Register <IMatchFilterFactory, MatchFilterFactory>(); composition.Register <IPlayerSummaryStatisticsDataSource, CachedPlayerSummaryStatisticsDataSource>(); composition.Register <ICacheablePlayerSummaryStatisticsDataSource, SqlServerPlayerSummaryStatisticsDataSource>(); composition.Register <IPlayerPerformanceStatisticsDataSource, CachedPlayerPerformanceStatisticsDataSource>(); composition.Register <ICacheablePlayerPerformanceStatisticsDataSource, SqlServerPlayerPerformanceStatisticsDataSource>(); composition.Register <IBestPlayerTotalStatisticsDataSource, CachedBestPlayerTotalStatisticsDataSource>(); composition.Register <ICacheableBestPlayerTotalStatisticsDataSource, SqlServerBestPlayerTotalStatisticsDataSource>(); composition.Register <IBestPlayerAverageStatisticsDataSource, CachedBestPlayerAverageStatisticsDataSource>(); composition.Register <ICacheableBestPlayerAverageStatisticsDataSource, SqlServerBestPlayerAverageStatisticsDataSource>(); composition.Register <ISchoolDataSource, SqlServerSchoolDataSource>(); // Security checks composition.Register <IAuthorizationPolicy <Club>, ClubAuthorizationPolicy>(); composition.Register <IAuthorizationPolicy <Competition>, CompetitionAuthorizationPolicy>(); composition.Register <IAuthorizationPolicy <MatchLocation>, MatchLocationAuthorizationPolicy>(); composition.Register <IAuthorizationPolicy <Match>, MatchAuthorizationPolicy>(); composition.Register <IAuthorizationPolicy <Tournament>, TournamentAuthorizationPolicy>(); composition.Register <IAuthorizationPolicy <Team>, TeamAuthorizationPolicy>(); }
static void Main(string[] args) { #region PS:这个示例使用了MemoryCache // PS:这个示例使用了MemoryCache,需要使用Nuget安装Polly.Caching.MemoryCache程序包,以及添加System.Runtime.Caching的引用。 //从运行结果可以看到,虽然三次执行都有结果,但系统只有第一次才需要执行函数,剩下两次都是直接从缓存中获取的结果。 //系统也提供了多种不同的过期策略: //Policy.Cache(memoryCacheProvider, new AbsoluteTtl(DateTimeOffset.Now.Date.AddDays(1))); // Policy.Cache(memoryCacheProvider, new SlidingTtl(TimeSpan.FromMinutes(5))); // 对于布式缓存,Polly也有默认的实现,只需要安装Polly.Caching.IdistributedCache程序包即可,它提供了SqlServer和Redis的支持 #endregion var memoryCache = new System.Runtime.Caching.MemoryCache("cache5566"); var memoryCacheProvider = new Polly.Caching.MemoryCache.MemoryCacheProvider(memoryCache); var cachePolicy = Policy.Cache(memoryCacheProvider, TimeSpan.FromMilliseconds(1000 * 5)); //Context.ExecutionKey就是cache的key var context11 = new Context("cache5566"); for (int i = 0; i < 3000; i++) { var cache = cachePolicy.Execute(_ => { Console.WriteLine("===重新获取的值==="); return(Guid.NewGuid().ToString()); }, context11); Console.WriteLine(cache); System.Threading.Thread.Sleep(500); } Console.WriteLine("======================"); Console.ReadKey(); Policy policy = Policy .Handle <Exception>() .CircuitBreaker(6, TimeSpan.FromSeconds(15));//连续出错6次之后熔断15秒(不会再去尝试执行业务代码)。 while (true) { Console.WriteLine("开始Execute"); try { policy.Execute(() => { Console.WriteLine("开始任务"); throw new Exception("出错"); Console.WriteLine("完成任务"); }); } catch (Exception ex) { Console.WriteLine("execute出错******"); } System.Threading.Thread.Sleep(500); } Policy .Timeout(3, onTimeout: (context, timespan, task) => { Console.WriteLine("超时了,奶奶的!"); }).Execute(() => { System.Threading.Thread.Sleep(5000); }); Policy .Handle <Exception>() .WaitAndRetry(new[] { TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(3) }, (exception, timeSpan, context) => { Console.WriteLine("dsfdsfds9999999999"); }).Execute(() => { Compute(); //Console.WriteLine("8888888888888"); }); var fallBackPolicy = Policy <List <string> > .Handle <Exception>() .Fallback <List <string> >(new List <string>() { "@@@@@", "#####" }); var fallBack = fallBackPolicy.Execute(() => { return(ThrowException()); }); fallBack.ForEach(Item => { Console.WriteLine(Item); }); #region MyRegion //try //{ // var politicaWaitAndRetry = Policy // .Handle<DivideByZeroException>() // .WaitAndRetry(new[] // { // TimeSpan.FromSeconds(1), // TimeSpan.FromMinutes(1), // TimeSpan.FromSeconds(5), // TimeSpan.FromSeconds(7) // }, ReportaError); // politicaWaitAndRetry.Execute(() => // { // ZeroExcepcion(); // }); //} //catch (Exception e) //{ // Console.WriteLine($"Executed Failed,Message:({e.Message})"); //} #endregion #region 重试次数 //try //{ // var retryTwoTimesPolicy = // Policy // .Handle<DivideByZeroException>() // .Or<ArgumentException>() // .Retry(3, (ex, count) => // { // Console.WriteLine("执行失败! 重试次数 {0}", count); // Console.WriteLine("异常来自 {0}", ex.GetType().Name); // }); // retryTwoTimesPolicy.Execute(() => // { // Compute(); // }); //} //catch (DivideByZeroException e) //{ // Console.WriteLine($"0-Excuted Failed,Message: ({e.Message})"); //} //catch(ArgumentException e) //{ // Console.WriteLine($"1-Excuted Failed,Message: ({e.Message})"); //} #endregion Console.ReadKey(); }
public static void Main(string[] args) { var host = new HostBuilder() .ConfigureAppConfiguration((hostContext, configApp) => { configApp.AddEnvironmentVariables(); configApp.AddEnvironmentVariables(prefix: "ASPNETCORE_"); configApp.SetBasePath(Directory.GetCurrentDirectory()); configApp.AddJsonFile("appsettings.json", optional: false); //configApp.AddJsonFile($"appsettings.{hostContext.HostingEnvironment.EnvironmentName}.json", optional: true); configApp.AddJsonFile($"appsettings.Development.json", optional: true); configApp.AddCommandLine(args); configApp.AddUserSecrets <NotificationServiceOptions>(); }) .ConfigureServices((hostContext, services) => { var configuration = hostContext.Configuration; services.AddSingleton <Polly.Caching.ISyncCacheProvider <CloudQueue>, DictionaryCacheProvider <CloudQueue> >(); services.AddSingleton <Polly.Registry.IPolicyRegistry <string>, Polly.Registry.PolicyRegistry>((serviceProvider) => { var registry = new PolicyRegistry(); registry.Add(NotificationService.PolicyRegistryKey, Policy.Cache <CloudQueue>(serviceProvider.GetRequiredService <ISyncCacheProvider <CloudQueue> >(), TimeSpan.FromMinutes(5))); return(registry); }); Policy .Handle <Exception>() .WaitAndRetry(5, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); services.AddDatabase(configuration); var key = configuration.GetValue <string>("NotifyApiKey"); services.AddScoped <NotificationClient>(sp => new NotificationClient(configuration.GetValue <string>("NotifyApiKey"))); services.AddScoped <INotifyService, NotifyService>(); services.AddLogging(); services.AddHostedService <NotificationService>(); services.Configure <NotificationServiceOptions>(configuration.GetSection(nameof(NotificationServiceOptions))); }) .ConfigureLogging((hostContext, configLogging) => { configLogging.AddConsole(); configLogging.AddDebug(); }) .Build(); using (host) { host.Start(); try { host.WaitForShutdown(); } catch (OperationCanceledException) { // This is expected: CTRL+C triggers this exception. } } }