/// <remarks> /// https://devblogs.microsoft.com/aspnet/queuebackgroundworkitem-to-reliably-schedule-and-run-background-processes-in-asp-net/ /// </remarks> private async Task <CancellationToken> UpdateStatistics(Guid taskId, Guid memberKey, string memberName, CancellationToken cancellationToken) { try { Logger.Info(GetType(), "Updating match statistics for all matches in {Type:l}.{Method:l}.", GetType(), nameof(UpdateStatistics)); var matchListings = (await _matchListingDataSource.ReadMatchListings(new MatchFilter { IncludeMatches = true, IncludeTournamentMatches = true, IncludeTournaments = false, UntilDate = DateTime.UtcNow }, MatchSortOrder.MatchDateEarliestFirst).ConfigureAwait(false)); _taskTracker.SetTarget(taskId, matchListings.Sum(x => x.MatchInnings.Count) + matchListings.Count); using (var connection = _databaseConnectionFactory.CreateDatabaseConnection()) { connection.Open(); foreach (var matchListing in matchListings) { if (cancellationToken.IsCancellationRequested) { Logger.Warn(GetType(), "Background task cancellation requested in {Type:l}.{Method:l}.", GetType(), nameof(UpdateStatistics)); } try { var match = await _matchDataSource.ReadMatchByRoute(matchListing.MatchRoute).ConfigureAwait(false); if (match != null) { using (var transaction = connection.BeginTransaction()) { await _statisticsRepository.DeletePlayerStatistics(match.MatchId.Value, transaction).ConfigureAwait(false); foreach (var innings in match.MatchInnings) { await _statisticsRepository.DeleteBowlingFigures(innings.MatchInningsId.Value, transaction).ConfigureAwait(false); innings.BowlingFigures = _bowlingFiguresCalculator.CalculateBowlingFigures(innings); await _statisticsRepository.UpdateBowlingFigures(innings, memberKey, memberName, transaction).ConfigureAwait(false); _taskTracker.IncrementCompletedBy(taskId, 1); } var hasPlayerData = _playerIdentityFinder.PlayerIdentitiesInMatch(match).Any(); if (hasPlayerData) { var statisticsData = _playerInMatchStatisticsBuilder.BuildStatisticsForMatch(match); await _statisticsRepository.UpdatePlayerStatistics(statisticsData, transaction).ConfigureAwait(false); } _taskTracker.IncrementCompletedBy(taskId, 1); transaction.Commit(); } } } catch (Exception ex) { Logger.Error(GetType(), "Error '{Error}' updating match statistics for '{MatchRoute}' in {Type:l}.{Method:l}.", ex.Message, matchListing.MatchRoute, GetType(), nameof(UpdateStatistics)); _taskTracker.IncrementErrorsBy(taskId, 1); } } using (var transaction = connection.BeginTransaction()) { await _statisticsRepository.UpdatePlayerProbability(null, transaction).ConfigureAwait(false); transaction.Commit(); } Logger.Info(GetType(), "Completed updating match statistics for all matches in {Type:l}.{Method:l}.", GetType(), nameof(UpdateStatistics)); } } catch (TaskCanceledException tce) { Logger.Error(GetType(), "Caught TaskCanceledException '{Message}' in {Type:l}.{Method:l}.", tce.Message, GetType(), nameof(UpdateStatistics)); } return(cancellationToken); }