/// <summary> /// Deletes the item from cache /// </summary> /// <param name="id">A string representing the id of the item in the cache to be deleted</param> /// <param name="cacheItemType">A cache item type</param> public override void CacheDeleteItem(string id, CacheItemType cacheItemType) { if (cacheItemType == CacheItemType.All || cacheItemType == CacheItemType.MarketDescription) { try { foreach (var fetchedVariant in _fetchedVariants) { if (fetchedVariant.Key.StartsWith(id)) { _fetchedVariants.TryRemove(id, out _); } } } catch (Exception e) { ExecutionLog.Warn($"Error deleting fetchedVariants for {id}", e); } if (_cache.Contains(id)) { CacheLog.Debug($"Delete variant market: {id}"); _cache.Remove(id); } } }
/// <summary> /// Asynchronously gets a <see cref="SportData"/> representing sport associated with the tournament specified by it's id. Note that the hierarchy will only contain the /// specified tournament and it's parent category not all categories / tournaments in the hierarchy /// </summary> /// <param name="tournamentId">A <see cref="URN"/> specifying the id of the tournament whose parent sport should be retrieved</param> /// <param name="cultures">A <see cref="IEnumerable{CultureInfo}"/> specifying the languages in which the data is returned</param> /// <returns>A <see cref="Task{SportData}"/> representing the asynchronous operation</returns> public async Task <SportData> GetSportForTournamentAsync(URN tournamentId, IEnumerable <CultureInfo> cultures) { Metric.Context("CACHE").Meter("SportDataCache->GetSportForTournamentAsync", Unit.Calls).Mark(); var cultureList = cultures as IList <CultureInfo> ?? cultures.ToList(); if (!await _semaphore.WaitAsyncSafe().ConfigureAwait(false)) { return(null); } var missingCultures = cultureList.Where(c => !FetchedCultures.Contains(c)).ToList(); try { if (missingCultures.Any()) { await FetchAndMergeAll(missingCultures, false).ConfigureAwait(false); } //return GetSportForTournamentFromCache(tournamentId, cultureList, false); } catch (Exception ex) { ExecutionLog.Warn($"An exception occurred while attempting to fetch sport data for tournament: id={tournamentId}, cultures={string.Join(",", cultureList)}. Exception: {ex}"); return(null); } finally { _semaphore.ReleaseSafe(); } var sport = GetSportForTournamentFromCache(tournamentId, cultureList, true); return(sport); }
/// <summary> /// Asynchronously gets the <see cref="VariantDescriptionCacheItem"/> specified by it's id. If the item is not found in local cache, all items for specified /// language are fetched from the service and stored/merged into the local cache. /// </summary> /// <param name="id">The id of the <see cref="VariantDescriptionCacheItem"/> instance to get</param> /// <param name="cultures">A <see cref="IEnumerable{CultureInfo}"/> specifying the languages which the returned item must contain</param> /// <returns>A <see cref="Task"/> representing the async operation</returns> /// <exception cref="CommunicationException">An error occurred while accessing the remote party</exception> /// <exception cref="DeserializationException">An error occurred while deserializing fetched data</exception> /// <exception cref="FormatException">An error occurred while mapping deserialized entities</exception> private async Task <VariantDescriptionCacheItem> GetVariantDescriptionInternalAsync(string id, IEnumerable <CultureInfo> cultures) { Guard.Argument(cultures, nameof(cultures)).NotNull();//.NotEmpty(); if (!cultures.Any()) { throw new ArgumentOutOfRangeException(nameof(cultures)); } var cultureList = cultures as List <CultureInfo> ?? cultures.ToList(); VariantDescriptionCacheItem description; if ((description = GetItemFromCache(id)) != null && !LanguageHelper.GetMissingCultures(cultureList, description.FetchedLanguages).Any()) { return(description); } try { await _semaphore.WaitAsync().ConfigureAwait(false); description = GetItemFromCache(id); var missingLanguages = LanguageHelper.GetMissingCultures(cultureList, description?.FetchedLanguages).ToList(); if (missingLanguages.Any()) { // dont call for already fetched languages missingLanguages = LanguageHelper.GetMissingCultures(missingLanguages, _fetchedLanguages).ToList(); } if (!missingLanguages.Any()) { return(description); } var cultureTaskDictionary = missingLanguages.ToDictionary(l => l, l => _dataRouterManager.GetVariantDescriptionsAsync(l)); await Task.WhenAll(cultureTaskDictionary.Values).ConfigureAwait(false); } catch (Exception ex) { var disposedException = ex as ObjectDisposedException; if (disposedException != null) { ExecutionLog.Warn($"An error occurred while fetching market descriptions because the object graph is being disposed. Object causing the exception: {disposedException.ObjectName}."); return(null); } throw; } finally { if (!_isDisposed) { _semaphore.ReleaseSafe(); } } return((description = GetItemFromCache(id)) != null && !LanguageHelper.GetMissingCultures(cultureList, description.FetchedLanguages).Any() ? description : null); }
/// <summary> /// Invoked when the <code>_timer</code> ticks in order to periodically fetch market descriptions for configured /// languages /// </summary> /// <param name="sender">The <see cref="ITimer" /> raising the event</param> /// <param name="e">A <see cref="EventArgs" /> instance containing the event data</param> private async void OnTimerElapsed(object sender, EventArgs e) { if (_isDisposed) { return; } //when the timer first elapses fetch data only for languages which were not yet fetched. On subsequent timer elapses fetch data for all configured languages var languagesToFetch = _prefetchLanguages .Where(language => !_fetchedLanguages.Contains(language) || _hasTimerElapsedOnce).ToList(); if (!languagesToFetch.Any()) { _hasTimerElapsedOnce = true; return; } try { _fetchedLanguages.Clear(); ExecutionLog.Debug( $"Loading invariant market descriptions for [{string.Join(",", languagesToFetch.Select(l => l.TwoLetterISOLanguageName))}] (timer)."); await GetMarketInternalAsync(0, languagesToFetch).ConfigureAwait(false); } catch (Exception ex) { if (ex is CommunicationException || ex is DeserializationException || ex is FormatException) { var languagesString = string.Join(",", languagesToFetch.Select(l => l.TwoLetterISOLanguageName)); ExecutionLog.Warn( $"An error occurred while periodically fetching market description of languages {languagesString}", ex); return; } var disposedException = ex as ObjectDisposedException; if (disposedException != null) { ExecutionLog.Warn( $"An error occurred while periodically fetching market descriptions because the object graph is being disposed. Object causing the exception:{disposedException.ObjectName}"); return; } if (ex is TaskCanceledException) { ExecutionLog.Warn( "An error occurred while periodically fetching market descriptions because the object graph is being disposed."); return; } ExecutionLog.Warn("An error occurred while periodically fetching market description.", ex); } _hasTimerElapsedOnce = true; }
/// <summary> /// Gets a <see cref="SportData"/> representing the sport specified by <code>id</code> in the languages specified by <code>cultures</code>, or a null reference /// if the specified sport does not exist, or it(or one of it's children) is not available in one of the requested languages /// </summary> /// <param name="id">A <see cref="URN"/> specifying the id of the sport to get</param> /// <param name="cultures">A <see cref="IEnumerable{CultrureInfo}"/> specifying the languages to which the sport must be translated</param> /// <returns>A <see cref="SportData"/> representing the requested sport translated into requested languages. </returns> private async Task <SportData> GetSportFromCacheAsync(URN id, IEnumerable <CultureInfo> cultures) { var cultureList = cultures as IList <CultureInfo> ?? cultures.ToList(); await FetchSportCategoriesIfNeededAsync(id, cultureList).ConfigureAwait(false); List <CategoryData> categories = null; lock (_mergeLock) { SportCI cachedSport; if (!(Sports.TryGetValue(id, out cachedSport) && cachedSport.HasTranslationsFor(cultureList))) { return(null); } try { if (cachedSport.CategoryIds != null) { categories = new List <CategoryData>(); foreach (var categoryId in cachedSport.CategoryIds) { CategoryCI cachedCategory; if (!(Categories.TryGetValue(categoryId, out cachedCategory) && cachedCategory.HasTranslationsFor(cultureList))) { ExecutionLog.Warn($"An error occurred while retrieving sport from cache.For sportId = {id} and lang =[{string.Join(",", cultureList)}] we are missing category {categoryId}."); continue; } categories.Add(new CategoryData( cachedCategory.Id, cachedCategory.Name.Where(t => cultureList.Contains(t.Key)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value), cachedCategory.CountryCode, cachedCategory.TournamentIds ?? new List <URN>())); } } } catch (Exception e) { ExecutionLog.Warn($"An error occured while retrieving sport from cache. id={id} and lang=[{string.Join(",", cultureList)}].", e); } return(new SportData( cachedSport.Id, cachedSport.Name.Where(t => cultureList.Contains(t.Key)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value), categories)); } }
/// <summary> /// Merges the provided descriptions with those found in cache /// </summary> /// <param name="culture">A <see cref="CultureInfo"/> specifying the language of the <code>descriptions</code></param> /// <param name="descriptions">A <see cref="IEnumerable{MarketDescriptionDTO}"/> containing market descriptions in specified language</param> private void Merge(CultureInfo culture, IEnumerable <MarketDescriptionDTO> descriptions) { Guard.Argument(culture, nameof(culture)).NotNull(); Guard.Argument(descriptions, nameof(descriptions)).NotNull();//.NotEmpty(); if (!descriptions.Any()) { throw new ArgumentOutOfRangeException(nameof(descriptions)); } var descriptionList = descriptions as List <MarketDescriptionDTO> ?? descriptions.ToList(); try { _semaphoreCacheMerge.Wait(); foreach (var marketDescription in descriptionList) { try { var cachedItem = _cache.GetCacheItem(marketDescription.Id.ToString()); if (cachedItem == null) { cachedItem = new CacheItem(marketDescription.Id.ToString(), MarketDescriptionCacheItem.Build(marketDescription, _mappingValidatorFactory, culture, CacheName)); _cache.Add(cachedItem, _cacheItemPolicy); } else { ((MarketDescriptionCacheItem)cachedItem.Value).Merge(marketDescription, culture); } } catch (Exception e) { if (!(e is InvalidOperationException)) { throw; } ExecutionLog.Warn($"Mapping validation for MarketDescriptionCacheItem failed. Id={marketDescription.Id}", e); } } _fetchedLanguages.Add(culture); } finally { if (!_isDisposed) { _semaphoreCacheMerge.Release(); } } }
/// <summary> /// Asynchronously loads the list of market descriptions from the Sports API /// </summary> /// <returns>Returns true if the action succeeded</returns> public async Task <bool> LoadMarketDescriptionsAsync() { try { ExecutionLog.Debug($"Loading variant market descriptions for [{string.Join(", ", _prefetchLanguages.Select(l => l.TwoLetterISOLanguageName))}] (user request)."); _fetchedLanguages.Clear(); await GetVariantDescriptionInternalAsync("0", _prefetchLanguages).ConfigureAwait(false); } catch (Exception ex) { ExecutionLog.Warn($"An error occurred while fetching market descriptions variant list. The exception:{ex.Message}"); return(false); } return(true); }
/// <summary> /// Asynchronously gets a <see cref="IEnumerable{SportData}"/> representing sport hierarchies for all sports supported by the feed. /// </summary> /// <param name="cultures">A <see cref="IEnumerable{CultureInfo}"/> specifying the languages in which the data is returned</param> /// <returns>A <see cref="Task{T}"/> representing the asynchronous operation</returns> public async Task <IEnumerable <SportData> > GetSportsAsync(IEnumerable <CultureInfo> cultures) { var cultureList = cultures as IList <CultureInfo> ?? cultures.ToList(); //Just lock - don't even check if all the required data is available if (!await _semaphore.WaitAsyncSafe().ConfigureAwait(false)) { return(null); } var missingCultures = cultureList.Where(c => !FetchedCultures.Contains(c)).ToList(); try { // we have all available data - return the requested info if (!missingCultures.Any()) { var sports = Sports.Keys.Select(sportId => { var sportFromCacheAsync = GetSportFromCacheAsync(sportId, cultureList); sportFromCacheAsync.ConfigureAwait(false); return(sportFromCacheAsync); }).ToList(); return(await Task.WhenAll(sports).ConfigureAwait(false)); } await FetchAndMergeAll(missingCultures, false).ConfigureAwait(false); return(await Task.WhenAll(Sports.Keys.Select(sportId => { var sportFromCacheAsync = GetSportFromCacheAsync(sportId, cultureList); sportFromCacheAsync.ConfigureAwait(false); return sportFromCacheAsync; }).ToList())); } catch (Exception ex) { ExecutionLog.Warn($"An exception occurred while attempting to fetch sports data for: {string.Join(",", missingCultures)}. Exception: {ex}"); throw; } finally { _semaphore.ReleaseSafe(); } }
/// <summary> /// Fetches the data for pre-configured languages and merges it to internally used dictionaries. First time the method is invoked it fetches the data only for missing languages. /// On subsequent calls data for all configured languages is fetched and dictionary are cleared before fetched data is added / merged /// </summary> /// <param name="sender">A <see cref="ITimer"/> invoking the method</param> /// <param name="e">The <see cref="EventArgs"/> providing additional information about the event which invoked the method</param> private async void OnTimerElapsed(object sender, EventArgs e) { if (!await _semaphore.WaitAsyncSafe().ConfigureAwait(false)) { return; } IList <CultureInfo> cultureInfos = new List <CultureInfo>(); try { var missingLanguages = _wasDataAutoFetched ? _requiredCultures : _requiredCultures.Where(c => !FetchedCultures.Any()).ToList(); cultureInfos = missingLanguages as IList <CultureInfo> ?? missingLanguages.ToList(); if (cultureInfos.Any()) { await FetchAndMergeAll(cultureInfos, _wasDataAutoFetched).ConfigureAwait(false); ExecutionLog.Info($"Sport data for languages [{string.Join(",", cultureInfos)}] successfully fetched and merged."); _wasDataAutoFetched = true; } } catch (FeedSdkException ex) { ExecutionLog.Warn($"An exception occurred while attempting to fetch sport data for: {string.Join(",", cultureInfos)}. Exception was: {ex}"); } catch (ObjectDisposedException) { ExecutionLog.Warn($"An exception occurred while attempting to fetch sport data for: {string.Join(",", cultureInfos)}. DataProvider was already disposed."); } catch (TaskCanceledException) { ExecutionLog.Warn($"An exception occurred while attempting to fetch sport data for: {string.Join(",", cultureInfos)}. Task canceled. DataProvider was already disposed."); } catch (Exception ex) { ExecutionLog.Warn($"An exception occurred while attempting to fetch sport data for: {string.Join(",", cultureInfos)}. Exception: {ex}"); } finally { _semaphore.ReleaseSafe(); } }
/// <summary> /// Merges the provided descriptions with those found in cache /// </summary> /// <param name="culture">A <see cref="CultureInfo" /> specifying the language of the <code>descriptions</code></param> /// <param name="descriptions"> /// A <see cref="IEnumerable{MarketDescriptionDTO}" /> containing market descriptions in /// specified language /// </param> private void Merge(CultureInfo culture, IEnumerable <VariantDescriptionDTO> descriptions) { Contract.Requires(culture != null); Contract.Requires(descriptions != null && descriptions.Any()); var descriptionList = descriptions as List <VariantDescriptionDTO> ?? descriptions.ToList(); try { _semaphoreCacheMerge.Wait(); foreach (var marketDescription in descriptionList) { var cachedItem = _cache.GetCacheItem(marketDescription.Id); if (cachedItem == null) { cachedItem = new CacheItem(marketDescription.Id, VariantDescriptionCacheItem.Build(marketDescription, _mappingValidatorFactory, culture, CacheName)); _cache.Add(cachedItem, new CacheItemPolicy()); } else { ((VariantDescriptionCacheItem)cachedItem.Value).Merge(marketDescription, culture); } } _fetchedLanguages.Add(culture); } catch (Exception e) { if (!(e is InvalidOperationException)) { throw; } ExecutionLog.Warn("Mapping validation for VariantDescriptionCacheItem failed.", e); } finally { if (!_isDisposed) { _semaphoreCacheMerge.Release(); } } }
/// <summary> /// Asynchronously gets a <see cref="CategoryData"/> instance /// </summary> /// <param name="id">A <see cref="URN"/> specifying the id of the category</param> /// <param name="cultures">A <see cref="IEnumerable{CultureInfo}"/> specifying the languages in which the data is returned</param> /// <returns>A <see cref="Task{SportData}"/> representing the asynchronous operation</returns> public async Task <CategoryData> GetCategoryAsync(URN id, IEnumerable <CultureInfo> cultures) { Metric.Context("CACHE").Meter("SportDataCache->GetCategoryAsync", Unit.Calls).Mark(); if (!await _semaphore.WaitAsyncSafe().ConfigureAwait(false)) { return(null); } var cultureList = cultures as IList <CultureInfo> ?? cultures.ToList(); var missingCultures = cultureList; CategoryCI categoryCI; try { if (Categories.TryGetValue(id, out categoryCI)) { if (categoryCI.HasTranslationsFor(cultureList)) { return(new CategoryData(id, categoryCI.Name.Where(t => cultureList.Contains(t.Key)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value), categoryCI.CountryCode, categoryCI.TournamentIds)); } missingCultures = cultureList.Where(c => !FetchedCultures.Contains(c)).ToList(); } if (missingCultures.Any()) { await FetchAndMergeAll(missingCultures, false).ConfigureAwait(false); } } catch (Exception ex) { ExecutionLog.Warn($"An exception occurred while attempting to fetch category data for: id={id}, cultures={string.Join(",", cultureList)}. Exception: {ex}"); } finally { _semaphore.ReleaseSafe(); } return(Categories.TryGetValue(id, out categoryCI) ? new CategoryData(id, categoryCI.Name.Where(t => cultureList.Contains(t.Key)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value), categoryCI.CountryCode, categoryCI.TournamentIds) : null); }
/// <summary> /// Merges the provided descriptions with those found in cache /// </summary> /// <param name="culture">A <see cref="CultureInfo" /> specifying the language of the <code>descriptions</code></param> /// <param name="description">A <see cref="MarketDescriptionDTO" /> containing market description in specified language</param> private void Merge(CultureInfo culture, MarketDescriptionDTO description) { Contract.Requires(culture != null); Contract.Requires(description != null); if (_isDisposed) { return; } try { _semaphoreCacheMerge.Wait(); var cachedItem = _cache.GetCacheItem(GetCacheKey(description.Id, description.Variant)); if (cachedItem == null) { cachedItem = new CacheItem(GetCacheKey(description.Id, description.Variant), MarketDescriptionCacheItem.Build(description, _mappingValidatorFactory, culture, CacheName)); _cache.Add(cachedItem, _cacheItemPolicy); } else { ((MarketDescriptionCacheItem)cachedItem.Value).Merge(description, culture); } } catch (Exception e) { if (!(e is InvalidOperationException)) { throw; } ExecutionLog.Warn("Mapping validation for MarketDescriptionCacheItem failed.", e); } finally { if (!_isDisposed) { _semaphoreCacheMerge.Release(); } } }
/// <summary> /// Invoked when the internally used timer elapses /// </summary> /// <param name="sender">A <see cref="object" /> representation of the <see cref="ITimer" /> raising the event</param> /// <param name="e">The <see cref="EventArgs" /> instance containing the event data</param> private async void OnTimerElapsed(object sender, EventArgs e) { //check what needs to be fetched, then go fetched by culture, (not by date) var datesToFetch = new List <DateTime>(); lock (_addLock) { var date = DateTime.Now; for (var i = 0; i < 3; i++) { if (_fetchedDates.Any(d => (date - d).TotalDays < 1)) { continue; } datesToFetch.Add(date); _fetchedDates.Add(date); date = date.AddDays(1); } } if (!datesToFetch.Any()) { return; } var culturesToFetch = _cultures.ToDictionary(ci => ci, ci => datesToFetch); foreach (var key in culturesToFetch) { try { var tasks = key.Value.Select(d => GetScheduleAsync(d, key.Key)).ToList(); await Task.WhenAll(tasks).ConfigureAwait(false); } catch (ObjectDisposedException ex) { ExecutionLog.Warn( $"Periodic events schedule retrieval failed because the instance {ex.ObjectName} is being disposed."); } }
/// <summary> /// Merges the provided descriptions with those found in cache /// </summary> /// <param name="culture">A <see cref="CultureInfo"/> specifying the language of the <code>descriptions</code></param> /// <param name="description">A <see cref="MarketDescriptionDTO"/> containing market description in specified language</param> private void Merge(CultureInfo culture, MarketDescriptionDTO description) { Guard.Argument(culture, nameof(culture)).NotNull(); Guard.Argument(description, nameof(description)).NotNull(); if (_isDisposed) { return; } try { _semaphoreCacheMerge.Wait(); var cachedItem = _cache.GetCacheItem(GetCacheKey(description.Id, description.Variant)); if (cachedItem == null) { cachedItem = new CacheItem(GetCacheKey(description.Id, description.Variant), MarketDescriptionCacheItem.Build(description, _mappingValidatorFactory, culture, CacheName)); _cache.Add(cachedItem, new CacheItemPolicy { SlidingExpiration = OperationManager.VariantMarketDescriptionCacheTimeout }); } else { ((MarketDescriptionCacheItem)cachedItem.Value).Merge(description, culture); } } catch (Exception e) { if (!(e is InvalidOperationException)) { throw; } ExecutionLog.Warn("Mapping validation for MarketDescriptionCacheItem failed.", e); } finally { _semaphoreCacheMerge.ReleaseSafe(); } }
/// <summary> /// Deletes the item from cache /// </summary> /// <param name="id">A string representing the id of the item in the cache to be deleted</param> /// <param name="cacheItemType">A cache item type</param> public override void CacheDeleteItem(string id, CacheItemType cacheItemType) { if (cacheItemType == CacheItemType.All || cacheItemType == CacheItemType.MarketDescription) { try { foreach (var fetchedVariant in _fetchedVariants) { if (fetchedVariant.Key.StartsWith(id)) { DateTime date; _fetchedVariants.TryRemove(id, out date); } } } catch (Exception e) { ExecutionLog.Warn($"Error deleting fetchedVariants for {id}", e); } _cache.Remove(id); } }
/// <summary> /// Adds the dto item to cache /// </summary> /// <param name="id">The identifier of the object</param> /// <param name="item">The item</param> /// <param name="culture">The culture</param> /// <param name="dtoType">Type of the dto</param> /// <param name="requester">The cache item which invoked request</param> /// <returns><c>true</c> if added, <c>false</c> otherwise</returns> protected override bool CacheAddDtoItem(URN id, object item, CultureInfo culture, DtoType dtoType, ISportEventCI requester) { //CacheLog.Debug($"Saving {id}."); if (_isDisposed) { return(false); } var saved = false; switch (dtoType) { case DtoType.MatchSummary: if (SaveCompetitorsFromSportEvent(item, culture)) { saved = true; } else { LogSavingDtoConflict(id, typeof(MatchDTO), item.GetType()); } break; case DtoType.RaceSummary: if (SaveCompetitorsFromSportEvent(item, culture)) { saved = true; } else { LogSavingDtoConflict(id, typeof(StageDTO), item.GetType()); } break; case DtoType.TournamentInfo: if (SaveCompetitorsFromSportEvent(item, culture)) { saved = true; } else { LogSavingDtoConflict(id, typeof(TournamentInfoDTO), item.GetType()); } break; case DtoType.SportEventSummary: if (SaveCompetitorsFromSportEvent(item, culture)) { saved = true; } else { LogSavingDtoConflict(id, typeof(SportEventSummaryDTO), item.GetType()); } break; case DtoType.Sport: break; case DtoType.Category: break; case DtoType.Tournament: break; case DtoType.PlayerProfile: var playerProfile = item as PlayerProfileDTO; if (playerProfile != null) { AddPlayerProfile(playerProfile, null, culture, true); saved = true; } else { LogSavingDtoConflict(id, typeof(PlayerProfileDTO), item.GetType()); } break; case DtoType.Competitor: var competitor = item as CompetitorDTO; if (competitor != null) { AddCompetitor(id, competitor, culture, true); saved = true; } else { LogSavingDtoConflict(id, typeof(CompetitorDTO), item.GetType()); } break; case DtoType.CompetitorProfile: var competitorProfile = item as CompetitorProfileDTO; if (competitorProfile != null) { AddCompetitorProfile(id, competitorProfile, culture, true); saved = true; } else { LogSavingDtoConflict(id, typeof(CompetitorProfileDTO), item.GetType()); } break; case DtoType.SimpleTeamProfile: var simpleTeamProfile = item as SimpleTeamProfileDTO; if (simpleTeamProfile != null) { AddCompetitorProfile(id, simpleTeamProfile, culture, true); saved = true; } else { LogSavingDtoConflict(id, typeof(SimpleTeamProfileDTO), item.GetType()); } break; case DtoType.MarketDescription: break; case DtoType.SportEventStatus: break; case DtoType.MatchTimeline: var matchTimeline = item as MatchTimelineDTO; if (matchTimeline != null) { saved = SaveCompetitorsFromSportEvent(matchTimeline.SportEvent, culture); } else { LogSavingDtoConflict(id, typeof(MatchTimelineDTO), item.GetType()); } break; case DtoType.TournamentSeasons: break; case DtoType.Fixture: if (SaveCompetitorsFromSportEvent(item, culture)) { saved = true; } else { LogSavingDtoConflict(id, typeof(FixtureDTO), item.GetType()); } break; case DtoType.SportList: break; case DtoType.SportEventSummaryList: break; case DtoType.MarketDescriptionList: break; case DtoType.VariantDescription: break; case DtoType.VariantDescriptionList: break; case DtoType.Lottery: break; case DtoType.LotteryDraw: break; case DtoType.LotteryList: break; case DtoType.BookingStatus: break; case DtoType.SportCategories: break; default: ExecutionLog.Warn($"Trying to add unchecked dto type: {dtoType} for id: {id}."); break; } //CacheLog.Debug($"Saving {id} COMPLETED."); return(saved); }
/// <summary> /// Adds the dto item to cache /// </summary> /// <param name="id">The identifier of the object</param> /// <param name="item">The item to be added</param> /// <param name="culture">The culture of the item</param> /// <param name="dtoType">Type of the dto</param> /// <param name="requester">The cache item which invoked request</param> /// <returns><c>true</c> if added, <c>false</c> otherwise</returns> /// <exception cref="System.ArgumentOutOfRangeException">dtoType - null</exception> protected override bool CacheAddDtoItem(URN id, object item, CultureInfo culture, DtoType dtoType, ISportEventCI requester) { if (_isDisposed) { return(false); } var saved = false; switch (dtoType) { case DtoType.Category: break; case DtoType.Competitor: break; case DtoType.CompetitorProfile: break; case DtoType.SimpleTeamProfile: break; case DtoType.Fixture: break; case DtoType.MarketDescription: var marketDescription = item as MarketDescriptionDTO; if (marketDescription != null) { //WriteLog($"Saving {marketDescription.Id} variant description for lang: [{culture.TwoLetterISOLanguageName}]."); Merge(culture, marketDescription); saved = true; //WriteLog($"Saving {marketDescription.Id} variant description for lang: [{culture.TwoLetterISOLanguageName}] COMPLETED."); } else { LogSavingDtoConflict(id, typeof(MarketDescriptionDTO), item.GetType(), ExecutionLog); } break; case DtoType.MarketDescriptionList: break; case DtoType.MatchSummary: break; case DtoType.MatchTimeline: break; case DtoType.PlayerProfile: break; case DtoType.RaceSummary: break; case DtoType.Sport: break; case DtoType.SportList: break; case DtoType.SportEventStatus: break; case DtoType.SportEventSummary: break; case DtoType.SportEventSummaryList: break; case DtoType.Tournament: break; case DtoType.TournamentInfo: break; case DtoType.TournamentSeasons: break; case DtoType.VariantDescription: break; case DtoType.VariantDescriptionList: break; case DtoType.Lottery: break; case DtoType.LotteryDraw: break; case DtoType.LotteryList: break; case DtoType.BookingStatus: break; case DtoType.SportCategories: break; case DtoType.AvailableSelections: break; default: ExecutionLog.Warn($"Trying to add unchecked dto type: {dtoType} for id: {id}."); break; } return(saved); }
/// <summary> /// Merges the provided descriptions with those found in cache /// </summary> /// <param name="culture">A <see cref="CultureInfo" /> specifying the language of the <code>descriptions</code></param> /// <param name="descriptions"> /// A <see cref="IEnumerable{MarketDescriptionDTO}" /> containing market descriptions in /// specified language /// </param> private void Merge(CultureInfo culture, IEnumerable <MarketDescriptionDTO> descriptions) { Contract.Requires(culture != null); Contract.Requires(descriptions != null && descriptions.Any()); var descriptionList = descriptions as List <MarketDescriptionDTO> ?? descriptions.ToList(); try { _semaphoreCacheMerge.Wait(); foreach (var marketDescription in descriptionList) { try { var cachedItem = _cache.GetCacheItem(marketDescription.Id.ToString()); if (cachedItem == null) { cachedItem = new CacheItem(marketDescription.Id.ToString(), MarketDescriptionCacheItem.Build(marketDescription, _mappingValidatorFactory, culture, CacheName)); _cache.Add(cachedItem, _cacheItemPolicy); } else { ((MarketDescriptionCacheItem)cachedItem.Value).Merge(marketDescription, culture); } } catch (Exception e) { if (!(e is InvalidOperationException)) { throw; } ExecutionLog.Warn( $"Mapping validation for MarketDescriptionCacheItem failed. Id={marketDescription.Id}", e); } } _fetchedLanguages.Add(culture); } finally { if (!_isDisposed) { _semaphoreCacheMerge.Release(); } } //var c = _cache.Count(); //ExecutionLog.Debug($"InvariantMarketDescriptionCache count: {c}."); //foreach (var keyValue in _cache.Where(s=>s.Key != null)) //{ // var ci = (MarketDescriptionCacheItem)keyValue.Value; // if (ci.Mappings != null) // { // foreach (var mapping in ci.Mappings) // { // if (mapping.OutcomeMappings != null) // { // foreach (var outcomeMapping in mapping.OutcomeMappings) // { // if (outcomeMapping.ProducerOutcomeNames.Count != ci.FetchedLanguages.Count) // { // ExecutionLog.Error($"Market {ci.Id}: problem with outcome mapping {outcomeMapping.OutcomeId} and mapped marketId {outcomeMapping.MarketId}"); // } // } // } // } // } //} //var cacheItem = _cache.First(); }
/// <summary> /// Gets a <see cref="SportData"/> representing the parent sport of the tournament specified by <code>tournamentId</code> in the languages specified by <code>cultures</code>, or a null reference /// if the specified sport does not exist, or it(or one of it's children) is not available in one of the requested languages /// </summary> /// <remarks> /// The returned <see cref="SportData"/> represents a sport with flattened hierarchy information - only one category and one tournament are found in the returned instance. /// </remarks> /// <param name="tournamentId">A <see cref="URN"/> specifying the tournament whose parent sport to get</param> /// <param name="cultures">A <see cref="IEnumerable{CultrureInfo}"/> specifying the languages to which the sport must be translated</param> /// <param name="fetchTournamentIfMissing">Indicates if the tournament should be fetched if not obtained via all tournaments request</param> /// <returns>A <see cref="SportData"/> representing the requested sport translated into requested languages</returns> private SportData GetSportForTournamentFromCache(URN tournamentId, IEnumerable <CultureInfo> cultures, bool fetchTournamentIfMissing) { var cultureList = cultures as IList <CultureInfo> ?? cultures.ToList(); TournamentInfoCI cachedTournament = null; if (!_sportEventCache.CacheHasItem(tournamentId, CacheItemType.Tournament)) { if (fetchTournamentIfMissing) { try { cachedTournament = (TournamentInfoCI)_sportEventCache.GetEventCacheItem(tournamentId); var unused = cachedTournament.GetCompetitorsAsync(cultureList).Result; } catch (Exception e) { ExecutionLog.Warn($"Error obtaining data for newly created tournament {tournamentId}.", e); return(null); } } } else { cachedTournament = (TournamentInfoCI)_sportEventCache.GetEventCacheItem(tournamentId); if (fetchTournamentIfMissing) { try { var unused = cachedTournament.GetCompetitorsAsync(cultureList).Result; } catch (Exception e) { ExecutionLog.Warn($"Error obtaining data for newly created tournament {tournamentId}.", e); } } } if (!(cachedTournament != null && cachedTournament.HasTranslationsFor(cultureList))) { return(null); } CategoryCI cachedCategory; if (!(Categories.TryGetValue(cachedTournament.GetCategoryIdAsync().Result, out cachedCategory) && cachedCategory.HasTranslationsFor(cultureList))) { return(null); } SportCI cachedSport; if (!(Sports.TryGetValue(cachedCategory.SportId, out cachedSport) && cachedSport.HasTranslationsFor(cultureList))) { return(null); } var category = new CategoryData( cachedCategory.Id, cachedCategory.Name.Where(k => cultureList.Contains(k.Key)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value), cachedCategory.CountryCode, new[] { cachedTournament.Id }); return(new SportData( cachedSport.Id, cachedSport.Name.Where(kvp => cultureList.Contains(kvp.Key)).ToDictionary(kvp => kvp.Key, kvp => kvp.Value), new[] { category })); }
/// <summary> /// Asynchronously gets the <see cref="MarketDescriptionCacheItem" /> specified by it's id. If the item is not found in /// local cache, all items for specified /// language are fetched from the service and stored/merged into the local cache. /// </summary> /// <param name="id">The id of the <see cref="MarketDescriptionCacheItem" /> instance to get</param> /// <param name="cultures"> /// A <see cref="IEnumerable{CultureInfo}" /> specifying the languages which the returned item must /// contain /// </param> /// <returns>A <see cref="Task" /> representing the async operation</returns> /// <exception cref="CommunicationException">An error occurred while accessing the remote party</exception> /// <exception cref="DeserializationException">An error occurred while deserializing fetched data</exception> /// <exception cref="FormatException">An error occurred while mapping deserialized entities</exception> private async Task <MarketDescriptionCacheItem> GetMarketInternalAsync(int id, IEnumerable <CultureInfo> cultures) { Contract.Requires(cultures != null && cultures.Any()); var cultureList = cultures as List <CultureInfo> ?? cultures.ToList(); MarketDescriptionCacheItem description; if ((description = GetItemFromCache(id)) != null && !LanguageHelper.GetMissingCultures(cultureList, description.FetchedLanguages).Any()) { return(description); } try { if (_isDisposed) { return(null); } //WriteLog($"Prefetching invariant market description for id={id} and langs: [{string.Join(",", cultureList.Select(s => s.TwoLetterISOLanguageName))}]."); await _semaphore.WaitAsync().ConfigureAwait(false); description = GetItemFromCache(id); var missingLanguages = LanguageHelper.GetMissingCultures(cultureList, description?.FetchedLanguages) .ToList(); if (missingLanguages.Any()) { // dont call for already fetched languages missingLanguages = LanguageHelper.GetMissingCultures(missingLanguages, _fetchedLanguages).ToList(); } if (!missingLanguages.Any()) { return(description); } //WriteLog($"Actually fetching invariant market description for id={id} and langs:[{string.Join(",", missingLanguages.Select(s => s.TwoLetterISOLanguageName))}]."); var cultureTaskDictionary = missingLanguages.ToDictionary(l => l, l => _dataRouterManager.GetMarketDescriptionsAsync(l)); await Task.WhenAll(cultureTaskDictionary.Values).ConfigureAwait(false); //WriteLog($"Actually fetching invariant market description for id={id} and langs:[{string.Join(",", missingLanguages.Select(s => s.TwoLetterISOLanguageName))}] COMPLETED."); //missingLanguages.ForEach(s => _fetchedLanguages.Add(s)); } catch (Exception ex) { var disposedException = ex as ObjectDisposedException; if (disposedException != null) { ExecutionLog.Warn( $"An error occurred while fetching market descriptions because the object graph is being disposed. Object causing the exception: {disposedException.ObjectName}."); return(null); } throw; } finally { if (!_isDisposed) { _semaphore.Release(); } } description = GetItemFromCache(id); return(description != null && !LanguageHelper.GetMissingCultures(cultureList, description.FetchedLanguages).Any() ? description : null); }
/// <summary> /// Adds the item to the cache /// </summary> /// <param name="id">The identifier of the item</param> /// <param name="item">The item to be added</param> /// <param name="culture">The culture of the data-transfer-object</param> /// <param name="dtoType">Type of the dto item</param> /// <param name="requester">The cache item which invoked request</param> /// <returns><c>true</c> if is added/updated, <c>false</c> otherwise</returns> protected override bool CacheAddDtoItem(URN id, object item, CultureInfo culture, DtoType dtoType, ISportEventCI requester) { if (_isDisposed) { return(false); } var saved = false; switch (dtoType) { case DtoType.Category: var category = item as CategoryDTO; if (category != null) { AddCategory(id, category, culture); saved = true; } else { LogSavingDtoConflict(id, typeof(CategoryDTO), item.GetType()); } break; case DtoType.Competitor: break; case DtoType.CompetitorProfile: break; case DtoType.SimpleTeamProfile: break; case DtoType.Fixture: var fixture = item as FixtureDTO; if (fixture?.Tournament != null) { AddSport(fixture.SportId, fixture.Tournament.Sport, culture); AddCategory(fixture.Tournament.Category.Id, fixture.Tournament.Category, fixture.SportId, new List <URN> { fixture.Tournament.Id }, culture); } else { LogSavingDtoConflict(id, typeof(FixtureDTO), item.GetType()); } break; case DtoType.MarketDescription: break; case DtoType.MatchSummary: var match = item as MatchDTO; if (match?.Tournament != null) { AddSport(match.SportId, match.Tournament.Sport, culture); AddCategory(match.Tournament.Category.Id, match.Tournament.Category, match.SportId, new List <URN> { match.Tournament.Id }, culture); } else { LogSavingDtoConflict(id, typeof(MatchDTO), item.GetType()); } break; case DtoType.MatchTimeline: var timeline = item as MatchTimelineDTO; if (timeline?.SportEvent != null) { AddDataFromSportEventSummary(timeline.SportEvent, culture); saved = true; } else { LogSavingDtoConflict(id, typeof(MatchTimelineDTO), item.GetType()); } break; case DtoType.PlayerProfile: break; case DtoType.RaceSummary: var stageDTO = item as StageDTO; if (stageDTO != null) { if (stageDTO.Status != null) { AddDataFromSportEventSummary(stageDTO, culture); } saved = true; } else { LogSavingDtoConflict(id, typeof(StageDTO), item.GetType()); } break; case DtoType.Sport: var sport = item as SportDTO; if (sport != null) { AddSport(id, sport, culture); saved = true; } else { LogSavingDtoConflict(id, typeof(SportDTO), item.GetType()); } break; case DtoType.SportList: var sportList = item as EntityList <SportDTO>; if (sportList != null) { foreach (var s in sportList.Items) { AddSport(s.Id, s, culture); } saved = true; } else { LogSavingDtoConflict(id, typeof(EntityList <SportDTO>), item.GetType()); } break; case DtoType.SportEventStatus: break; case DtoType.SportEventSummary: var summary = item as SportEventSummaryDTO; if (summary != null) { AddDataFromSportEventSummary(summary, culture); saved = true; } else { LogSavingDtoConflict(id, typeof(SportEventSummaryDTO), item.GetType()); } break; case DtoType.SportEventSummaryList: var summaryList = item as EntityList <SportEventSummaryDTO>; if (summaryList != null) { foreach (var s in summaryList.Items) { AddDataFromSportEventSummary(s, culture); } saved = true; } else { LogSavingDtoConflict(id, typeof(EntityList <SportEventSummaryDTO>), item.GetType()); } break; case DtoType.Tournament: var tour = item as TournamentDTO; if (tour != null) { AddSport(tour.Sport.Id, tour.Sport, culture); AddCategory(tour.Category.Id, tour.Category, tour.Sport.Id, new List <URN> { tour.Id }, culture); } else { LogSavingDtoConflict(id, typeof(SportDTO), item.GetType()); } break; case DtoType.TournamentInfo: var tourInfo = item as TournamentInfoDTO; if (tourInfo != null) { AddSport(tourInfo.SportId, tourInfo.Sport, culture); AddCategory(tourInfo.Category.Id, tourInfo.Category, tourInfo.Sport.Id, new List <URN> { tourInfo.Id }, culture); } else { LogSavingDtoConflict(id, typeof(SportDTO), item.GetType()); } break; case DtoType.TournamentSeasons: var tourSeasons = item as TournamentSeasonsDTO; if (tourSeasons?.Tournament != null) { var tourSeasonsTournament = tourSeasons.Tournament; AddSport(tourSeasonsTournament.SportId, tourSeasonsTournament.Sport, culture); AddCategory(tourSeasonsTournament.Category.Id, tourSeasonsTournament.Category, tourSeasonsTournament.Sport.Id, new List <URN> { tourSeasonsTournament.Id }, culture); saved = true; } else { LogSavingDtoConflict(id, typeof(TournamentInfoDTO), item.GetType()); } break; case DtoType.MarketDescriptionList: break; case DtoType.VariantDescription: break; case DtoType.VariantDescriptionList: break; case DtoType.Lottery: var lottery = item as LotteryDTO; if (lottery != null) { AddDataFromSportEventSummary(lottery, culture); //if (lottery.DrawEvents == null || !lottery.DrawEvents.Any()) //{ // _dataRouterManager.GetLotteryScheduleAsync(lottery.Id, culture); //} //else //{ // _dataRouterManager.GetDrawFixtureAsync(lottery.DrawEvents.First().Id, culture); //} saved = true; } else { LogSavingDtoConflict(id, typeof(LotteryDTO), item.GetType()); } break; case DtoType.LotteryDraw: var lotteryDraw = item as DrawDTO; if (lotteryDraw != null) { AddDataFromSportEventSummary(lotteryDraw, culture); saved = true; } else { LogSavingDtoConflict(id, typeof(DrawDTO), item.GetType()); } break; case DtoType.LotteryList: var lotteryList = item as EntityList <LotteryDTO>; if (lotteryList != null) { foreach (var s in lotteryList.Items) { AddDataFromSportEventSummary(s, culture); } saved = true; } else { LogSavingDtoConflict(id, typeof(EntityList <LotteryDTO>), item.GetType()); } break; case DtoType.SportCategories: var sportCategories = item as SportCategoriesDTO; if (sportCategories != null) { AddSport(sportCategories.Sport.Id, sportCategories, culture); AddCategories(sportCategories, culture); } else { LogSavingDtoConflict(id, typeof(SportCategoriesDTO), item.GetType()); } break; case DtoType.BookingStatus: break; case DtoType.AvailableSelections: break; default: ExecutionLog.Warn($"Trying to add unchecked dto type: {dtoType} for id: {id}."); break; } return(saved); }
/// <summary> /// Adds the dto item to cache /// </summary> /// <param name="id">The identifier of the object</param> /// <param name="item">The item</param> /// <param name="culture">The culture</param> /// <param name="dtoType">Type of the dto</param> /// <param name="requester">The cache item which invoked request</param> /// <returns><c>true</c> if added, <c>false</c> otherwise</returns> protected override bool CacheAddDtoItem(URN id, object item, CultureInfo culture, DtoType dtoType, ISportEventCI requester) { if (_isDisposed) { return(false); } var saved = false; switch (dtoType) { case DtoType.Category: break; case DtoType.Competitor: break; case DtoType.CompetitorProfile: break; case DtoType.SimpleTeamProfile: break; case DtoType.Fixture: var fixtureDTO = item as FixtureDTO; if (fixtureDTO != null) { if (fixtureDTO.Status != null) { AddSportEventStatus(id, new SportEventStatusCI(null, fixtureDTO.Status), "Fixture"); } saved = true; } else { LogSavingDtoConflict(id, typeof(FixtureDTO), item.GetType()); } break; case DtoType.MarketDescription: break; case DtoType.MatchSummary: var matchDTO = item as MatchDTO; if (matchDTO != null) { if (matchDTO.Status != null) { AddSportEventStatus(id, new SportEventStatusCI(null, matchDTO.Status), "Match"); } saved = true; } else { LogSavingDtoConflict(id, typeof(MatchDTO), item.GetType()); } break; case DtoType.MatchTimeline: var matchTimelineDTO = item as MatchTimelineDTO; if (matchTimelineDTO != null) { if (matchTimelineDTO.SportEventStatus != null) { AddSportEventStatus(id, new SportEventStatusCI(null, matchTimelineDTO.SportEventStatus), "MatchTimeline"); } saved = true; } else { LogSavingDtoConflict(id, typeof(MatchTimelineDTO), item.GetType()); } break; case DtoType.PlayerProfile: break; case DtoType.RaceSummary: var stageDTO = item as StageDTO; if (stageDTO != null) { if (stageDTO.Status != null) { AddSportEventStatus(id, new SportEventStatusCI(null, stageDTO.Status), "Stage"); } saved = true; } else { LogSavingDtoConflict(id, typeof(StageDTO), item.GetType()); } break; case DtoType.Sport: break; case DtoType.SportList: break; case DtoType.SportEventStatus: var sportEventStatusDTO = item as SportEventStatusDTO; if (sportEventStatusDTO != null) { AddSportEventStatus(id, new SportEventStatusCI(sportEventStatusDTO, null), "OddsChange"); saved = true; } else { LogSavingDtoConflict(id, typeof(SportEventStatusDTO), item.GetType()); } break; case DtoType.SportEventSummary: var competitionDTO = item as CompetitionDTO; if (competitionDTO != null) { if (competitionDTO.Status != null) { AddSportEventStatus(id, new SportEventStatusCI(null, competitionDTO.Status), "SportEventSummary"); } saved = true; } break; case DtoType.SportEventSummaryList: var summaryList = item as EntityList <SportEventSummaryDTO>; if (summaryList != null) { foreach (var s in summaryList.Items) { var compDTO = s as CompetitionDTO; if (compDTO?.Status != null) { AddSportEventStatus(id, new SportEventStatusCI(null, compDTO.Status), "SportEventSummaryList"); } } saved = true; } else { LogSavingDtoConflict(id, typeof(EntityList <SportEventSummaryDTO>), item.GetType()); } break; case DtoType.Tournament: break; case DtoType.TournamentInfo: break; case DtoType.TournamentSeasons: break; case DtoType.MarketDescriptionList: break; case DtoType.VariantDescription: break; case DtoType.VariantDescriptionList: break; case DtoType.Lottery: break; case DtoType.LotteryDraw: break; case DtoType.LotteryList: break; case DtoType.BookingStatus: break; case DtoType.SportCategories: break; default: ExecutionLog.Warn($"Trying to add unchecked dto type: {dtoType} for id: {id}."); break; } //CacheLog.Debug($"Saving {id} COMPLETED. Saved={saved}."); return(saved); }
/// <summary> /// Adds the item to the cache /// </summary> /// <param name="id">The identifier of the item</param> /// <param name="item">The item to be add</param> /// <param name="culture">The culture of the data-transfer-object</param> /// <param name="dtoType">Type of the cache item</param> /// <param name="requester">The cache item which invoked request</param> /// <returns><c>true</c> if is added/updated, <c>false</c> otherwise</returns> /// <exception cref="NotImplementedException"></exception> protected override bool CacheAddDtoItem(URN id, object item, CultureInfo culture, DtoType dtoType, ISportEventCI requester) { if (_isDisposed) { return(false); } var saved = false; switch (dtoType) { case DtoType.Category: break; case DtoType.Competitor: break; case DtoType.CompetitorProfile: break; case DtoType.SimpleTeamProfile: break; case DtoType.Fixture: var fixture = item as FixtureDTO; if (fixture != null) { AddSportEvent(id, fixture, culture, requester, dtoType); saved = true; } else { LogSavingDtoConflict(id, typeof(FixtureDTO), item.GetType()); } break; case DtoType.MarketDescription: break; case DtoType.MatchSummary: var match = item as MatchDTO; if (match != null) { AddSportEvent(id, match, culture, requester, dtoType); if (match.Tournament != null) { var ti = new TournamentInfoDTO(match.Tournament); AddSportEvent(ti.Id, ti, culture, requester, dtoType); } saved = true; } else { LogSavingDtoConflict(id, typeof(MatchDTO), item.GetType()); } break; case DtoType.MatchTimeline: var timeline = item as MatchTimelineDTO; if (timeline != null) { AddMatchTimeLine(timeline, culture, requester, dtoType); saved = true; } else { LogSavingDtoConflict(id, typeof(MatchTimelineDTO), item.GetType()); } break; case DtoType.PlayerProfile: break; case DtoType.RaceSummary: var stage = item as StageDTO; if (stage != null) { AddSportEvent(id, stage, culture, requester, dtoType); if (stage.Tournament != null) { var ti = new TournamentInfoDTO(stage.Tournament); AddSportEvent(ti.Id, ti, culture, requester, dtoType); } saved = true; } else { LogSavingDtoConflict(id, typeof(StageDTO), item.GetType()); } break; case DtoType.Sport: var sport = item as SportDTO; if (sport != null) { SaveTournamentDataFromSport(sport, culture); saved = true; } else { LogSavingDtoConflict(id, typeof(SportDTO), item.GetType()); } break; case DtoType.SportList: var sportEntityList = item as EntityList <SportDTO>; if (sportEntityList != null) { foreach (var sportDTO in sportEntityList.Items) { SaveTournamentDataFromSport(sportDTO, culture); } saved = true; } else { LogSavingDtoConflict(id, typeof(EntityList <SportDTO>), item.GetType()); } break; case DtoType.SportEventStatus: break; case DtoType.SportEventSummary: var tourInfo = item as TournamentInfoDTO; if (tourInfo != null) { SaveTournamentDataToSportEventCache(tourInfo, tourInfo.CurrentSeason?.Id, culture); if (tourInfo.Season != null) { SaveTournamentDataToSportEventCache(tourInfo, tourInfo.Season?.Id, culture); } break; } var summary = item as SportEventSummaryDTO; if (summary != null) { AddSportEvent(id, summary, culture, requester, dtoType); saved = true; } else { LogSavingDtoConflict(id, typeof(SportEventSummaryDTO), item.GetType()); } break; case DtoType.SportEventSummaryList: var summaryList = item as EntityList <SportEventSummaryDTO>; if (summaryList != null) { foreach (var s in summaryList.Items) { var tourInfosDTO = s as TournamentInfoDTO; if (tourInfosDTO != null) { SaveTournamentDataToSportEventCache(tourInfosDTO, tourInfosDTO.CurrentSeason?.Id, culture); if (tourInfosDTO.Season != null) { SaveTournamentDataToSportEventCache(tourInfosDTO, tourInfosDTO.Season?.Id, culture); } continue; } AddSportEvent(s.Id, s, culture, requester, dtoType); } saved = true; } else { LogSavingDtoConflict(id, typeof(EntityList <SportEventSummaryDTO>), item.GetType()); } break; case DtoType.Tournament: var t = item as TournamentDTO; if (t != null) { var ti = new TournamentInfoDTO(t); AddSportEvent(id, ti, culture, requester, dtoType); saved = true; } else { LogSavingDtoConflict(id, typeof(TournamentDTO), item.GetType()); } break; case DtoType.TournamentInfo: var tour = item as TournamentInfoDTO; if (tour != null) { AddSportEvent(id, tour, culture, requester, dtoType); saved = true; } else { LogSavingDtoConflict(id, typeof(TournamentInfoDTO), item.GetType()); } break; case DtoType.TournamentSeasons: var tourSeasons = item as TournamentSeasonsDTO; if (tourSeasons?.Tournament != null) { AddSportEvent(id, tourSeasons.Tournament, culture, requester, dtoType); var cacheItem = (TournamentInfoCI)_sportEventCacheItemFactory.Get(Cache.Get(id.ToString())); cacheItem.Merge(tourSeasons, culture, true); if (tourSeasons.Seasons != null && tourSeasons.Seasons.Any()) { foreach (var season in tourSeasons.Seasons) { AddSportEvent(season.Id, new TournamentInfoDTO(season), culture, null, dtoType); } } saved = true; } else { LogSavingDtoConflict(id, typeof(TournamentInfoDTO), item.GetType()); } break; case DtoType.MarketDescriptionList: break; case DtoType.VariantDescription: break; case DtoType.VariantDescriptionList: break; case DtoType.Lottery: var lottery = item as LotteryDTO; if (lottery != null) { AddSportEvent(id, lottery, culture, requester, dtoType); saved = true; } else { LogSavingDtoConflict(id, typeof(TournamentInfoDTO), item.GetType()); } break; case DtoType.LotteryDraw: var draw = item as DrawDTO; if (draw != null) { AddSportEvent(id, draw, culture, requester, dtoType); saved = true; } else { LogSavingDtoConflict(id, typeof(TournamentInfoDTO), item.GetType()); } break; case DtoType.LotteryList: var lotteryList = item as EntityList <LotteryDTO>; if (lotteryList != null && lotteryList.Items.Any()) { foreach (var l in lotteryList.Items) { AddSportEvent(l.Id, l, culture, requester, dtoType); } saved = true; } else { LogSavingDtoConflict(id, typeof(TournamentInfoDTO), item.GetType()); } break; case DtoType.BookingStatus: if (Cache.Contains(id.ToString())) { var e = Cache.Get(id.ToString()); var comp = e as CompetitionCI; comp?.Book(); } break; case DtoType.SportCategories: break; default: ExecutionLog.Warn($"Trying to add unchecked dto type:{dtoType} for id: {id}."); break; } return(saved); }
/// <summary> /// Invoked when the internally used timer elapses /// </summary> /// <param name="sender">A <see cref="object"/> representation of the <see cref="ITimer"/> raising the event</param> /// <param name="e">The <see cref="EventArgs"/> instance containing the event data</param> private async void OnTimerElapsed(object sender, EventArgs e) { //check what needs to be fetched, then go fetched by culture, (not by date) var datesToFetch = new List <DateTime>(); lock (_addLock) { var date = DateTime.Now; for (var i = 0; i < 3; i++) { if (_fetchedDates.Any(d => (date - d).TotalDays < 1)) { continue; } datesToFetch.Add(date); _fetchedDates.Add(date); date = date.AddDays(1); } } if (!datesToFetch.Any()) { return; } var culturesToFetch = _cultures.ToDictionary(ci => ci, ci => datesToFetch); foreach (var key in culturesToFetch) { try { var tasks = key.Value.Select(d => GetScheduleAsync(d, key.Key)).ToList(); await Task.WhenAll(tasks).ConfigureAwait(false); } catch (ObjectDisposedException ex) { ExecutionLog.Warn($"Periodic events schedule retrieval failed because the instance {ex.ObjectName} is being disposed."); } catch (TaskCanceledException) { ExecutionLog.Warn("Periodic events schedule retrieval failed because the instance is being disposed."); } catch (FeedSdkException ex) { ExecutionLog.Warn($"An exception occurred while attempting to retrieve schedule. Exception was: {ex}"); } catch (AggregateException ex) { var baseException = ex.GetBaseException(); if (baseException.GetType() == typeof(ObjectDisposedException)) { ExecutionLog.Warn($"Error happened during fetching schedule, because the instance {((ObjectDisposedException) baseException).ObjectName} is being disposed."); } } catch (Exception ex) { ExecutionLog.Warn($"An exception occurred while attempting to retrieve schedule. Exception: {ex}"); } } }