Example #1
0
        /// <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.LogWarning($"Error deleting fetchedVariants for {id}", e);
                }

                if (_cache.Contains(id))
                {
                    CacheLog.LogDebug($"Delete variant market: {id}");
                    _cache.Remove(id);
                }
            }
        }
Example #2
0
        /// <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();

            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.LogWarning($"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();
                }
            }

            return((description = GetItemFromCache(id)) != null && !LanguageHelper.GetMissingCultures(cultureList, description.FetchedLanguages).Any()
                ? description
                : null);
        }
 /// <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.LogDebug($"Loading invariant market descriptions for [{string.Join(",", _prefetchLanguages.Select(l => l.TwoLetterISOLanguageName))}] (user request).");
         _fetchedLanguages.Clear();
         await GetMarketInternalAsync(0, _prefetchLanguages).ConfigureAwait(false);
     }
     catch (Exception ex)
     {
         ExecutionLog.LogWarning($"An error occurred while fetching market descriptions. The exception:{ex.Message}");
         return(false);
     }
     return(true);
 }
Example #4
0
        /// <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 async Task MergeAsync(CultureInfo culture, IEnumerable <MarketDescriptionDTO> descriptions)
        {
            Guard.Argument(culture, nameof(culture)).NotNull();
            Guard.Argument(descriptions, nameof(descriptions)).NotNull().NotEmpty();

            var descriptionList = descriptions as List <MarketDescriptionDTO> ?? descriptions.ToList();

            try
            {
                await _semaphoreCacheMerge.WaitAsync().ConfigureAwait(false);

                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.LogWarning(e, $"Mapping validation for MarketDescriptionCacheItem failed. Id={marketDescription.Id}");
                    }
                }
                _fetchedLanguages.Add(culture);
            }
            finally
            {
                if (!_isDisposed)
                {
                    _semaphoreCacheMerge.ReleaseSafe();
                }
            }
        }
        /// <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.LogDebug($"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.LogWarning($"An error occurred while periodically fetching market description of languages {languagesString}", ex);
                    return;
                }
                var disposedException = ex as ObjectDisposedException;
                if (disposedException != null)
                {
                    ExecutionLog.LogWarning($"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.LogWarning("An error occurred while periodically fetching market descriptions because the object graph is being disposed.");
                    return;
                }
                ExecutionLog.LogWarning("An error occurred while periodically fetching market description.", ex);
            }

            _hasTimerElapsedOnce = true;
        }
        /// <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 async Task MergeAsync(CultureInfo culture, MarketDescriptionDTO description)
        {
            Guard.Argument(culture, nameof(culture)).NotNull();
            Guard.Argument(description, nameof(description)).NotNull();

            if (_isDisposed)
            {
                return;
            }

            try
            {
                await _semaphoreCacheMerge.WaitAsync().ConfigureAwait(false);

                var cacheId    = GetCacheKey(description.Id, description.Variant);
                var cachedItem = _cache.GetCacheItem(cacheId);
                if (cachedItem == null)
                {
                    cachedItem = new CacheItem(cacheId, 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.LogWarning(e, "Mapping validation for MarketDescriptionCacheItem failed.");
            }
            finally
            {
                _semaphoreCacheMerge.ReleaseSafe();
            }
        }
Example #7
0
        /// <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)
        {
            Guard.Argument(culture, nameof(culture)).NotNull();
            Guard.Argument(descriptions, nameof(descriptions)).NotNull().NotEmpty();

            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.LogWarning("Mapping validation for VariantDescriptionCacheItem failed.", e);
            }
            finally
            {
                if (!_isDisposed)
                {
                    _semaphoreCacheMerge.Release();
                }
            }
        }
        /// <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, _cacheItemPolicy);
                }
                else
                {
                    ((MarketDescriptionCacheItem)cachedItem.Value).Merge(description, culture);
                }
            }
            catch (Exception e)
            {
                if (!(e is InvalidOperationException))
                {
                    throw;
                }
                ExecutionLog.LogWarning("Mapping validation for MarketDescriptionCacheItem failed.", e);
            }
            finally
            {
                if (!_isDisposed)
                {
                    _semaphoreCacheMerge.Release();
                }
            }
        }
        /// <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="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;

            case DtoType.TournamentInfoList:
                break;

            default:
                ExecutionLog.LogWarning($"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.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;

            case DtoType.AvailableSelections:
                break;

            case DtoType.TournamentInfoList:
                break;

            default:
                ExecutionLog.LogWarning($"Trying to add unchecked dto type: {dtoType} for id: {id}.");
                break;
            }
            return(saved);
        }
        /// <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)
        {
            Guard.Argument(cultures, nameof(cultures)).NotNull().NotEmpty();

            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.LogWarning($"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 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)
        {
            Guard.Argument(id, nameof(id)).NotNull();
            Guard.Argument(item, nameof(item)).NotNull();

            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.SportEventStatus != null)
                    {
                        AddSportEventStatus(id, new SportEventStatusCI(null, fixtureDTO.SportEventStatus), fixtureDTO.StatusOnEvent, "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.SportEventStatus != null)
                    {
                        AddSportEventStatus(id, new SportEventStatusCI(null, matchDTO.SportEventStatus), matchDTO.StatusOnEvent, "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), matchTimelineDTO.SportEvent.StatusOnEvent, "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.SportEventStatus != null)
                    {
                        AddSportEventStatus(id, new SportEventStatusCI(null, stageDTO.SportEventStatus), stageDTO.StatusOnEvent, "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), sportEventStatusDTO.Status.ToString(), "OddsChange");
                    saved = true;
                }
                else
                {
                    LogSavingDtoConflict(id, typeof(SportEventStatusDTO), item.GetType());
                }
                break;

            case DtoType.SportEventSummary:
                var competitionDTO = item as CompetitionDTO;
                if (competitionDTO != null)
                {
                    if (competitionDTO.SportEventStatus != null)
                    {
                        AddSportEventStatus(id, new SportEventStatusCI(null, competitionDTO.SportEventStatus), competitionDTO.StatusOnEvent, "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?.SportEventStatus != null)
                        {
                            AddSportEventStatus(id, new SportEventStatusCI(null, compDTO.SportEventStatus), s.StatusOnEvent, "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;

            case DtoType.AvailableSelections:
                break;

            case DtoType.TournamentInfoList:
                break;

            default:
                ExecutionLog.LogWarning($"Trying to add unchecked dto type: {dtoType} for id: {id}.");
                break;
            }
            //CacheLog.LogDebug($"Saving {id} COMPLETED. Saved={saved}.");
            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)
        {
            Guard.Argument(culture, nameof(culture)).NotNull();
            Guard.Argument(descriptions, nameof(descriptions)).NotNull().NotEmpty();

            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.LogWarning($"Mapping validation for MarketDescriptionCacheItem failed. Id={marketDescription.Id}", e);
                    }
                }
                _fetchedLanguages.Add(culture);
            }
            finally
            {
                if (!_isDisposed)
                {
                    _semaphoreCacheMerge.Release();
                }
            }

            //var c = _cache.Count();
            //ExecutionLog.LogDebug($"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.LogError($"Market {ci.Id}: problem with outcome mapping {outcomeMapping.OutcomeId} and mapped marketId {outcomeMapping.MarketId}");
            //                    }
            //                }
            //            }
            //        }
            //    }
            //}
            //var cacheItem = _cache.First();
        }
        /// <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 async Task <bool> CacheAddDtoItemAsync(URN id, object item, CultureInfo culture, DtoType dtoType, ISportEventCI requester)
        {
            if (_isDisposed)
            {
                return(false);
            }

            var saved = false;

            switch (dtoType)
            {
            case DtoType.MatchSummary:
                var competitorsSaved1 = await SaveCompetitorsFromSportEventAsync(item, culture).ConfigureAwait(false);

                if (competitorsSaved1)
                {
                    saved = true;
                }
                else
                {
                    LogSavingDtoConflict(id, typeof(MatchDTO), item.GetType());
                }
                break;

            case DtoType.RaceSummary:
                var competitorsSaved2 = await SaveCompetitorsFromSportEventAsync(item, culture).ConfigureAwait(false);

                if (competitorsSaved2)
                {
                    saved = true;
                }
                else
                {
                    LogSavingDtoConflict(id, typeof(StageDTO), item.GetType());
                }
                break;

            case DtoType.TournamentInfo:
                var competitorsSaved3 = await SaveCompetitorsFromSportEventAsync(item, culture).ConfigureAwait(false);

                if (competitorsSaved3)
                {
                    saved = true;
                }
                else
                {
                    LogSavingDtoConflict(id, typeof(TournamentInfoDTO), item.GetType());
                }
                break;

            case DtoType.SportEventSummary:
                var competitorsSaved4 = await SaveCompetitorsFromSportEventAsync(item, culture).ConfigureAwait(false);

                if (competitorsSaved4)
                {
                    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)
                {
                    await AddPlayerProfileAsync(playerProfile, null, culture, true).ConfigureAwait(false);

                    saved = true;
                }
                else
                {
                    LogSavingDtoConflict(id, typeof(PlayerProfileDTO), item.GetType());
                }
                break;

            case DtoType.Competitor:
                var competitor = item as CompetitorDTO;
                if (competitor != null)
                {
                    await AddCompetitorAsync(id, competitor, culture, true).ConfigureAwait(false);

                    saved = true;
                }
                else
                {
                    LogSavingDtoConflict(id, typeof(CompetitorDTO), item.GetType());
                }
                break;

            case DtoType.CompetitorProfile:
                var competitorProfile = item as CompetitorProfileDTO;
                if (competitorProfile != null)
                {
                    await AddCompetitorProfileAsync(id, competitorProfile, culture, true).ConfigureAwait(false);

                    saved = true;
                }
                else
                {
                    LogSavingDtoConflict(id, typeof(CompetitorProfileDTO), item.GetType());
                }
                break;

            case DtoType.SimpleTeamProfile:
                var simpleTeamProfile = item as SimpleTeamProfileDTO;
                if (simpleTeamProfile != null)
                {
                    await AddCompetitorProfileAsync(id, simpleTeamProfile, culture, true).ConfigureAwait(false);

                    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 = await SaveCompetitorsFromSportEventAsync(matchTimeline.SportEvent, culture).ConfigureAwait(false);
                }
                else
                {
                    LogSavingDtoConflict(id, typeof(MatchTimelineDTO), item.GetType());
                }
                break;

            case DtoType.TournamentSeasons:
                var tournamentSeason = item as TournamentSeasonsDTO;
                if (tournamentSeason?.Tournament != null)
                {
                    await SaveCompetitorsFromSportEventAsync(tournamentSeason.Tournament, culture).ConfigureAwait(false);

                    saved = true;
                }
                break;

            case DtoType.Fixture:
                var competitorsSaved5 = await SaveCompetitorsFromSportEventAsync(item, culture).ConfigureAwait(false);

                if (competitorsSaved5)
                {
                    saved = true;
                }
                else
                {
                    LogSavingDtoConflict(id, typeof(FixtureDTO), item.GetType());
                }
                break;

            case DtoType.SportList:
                break;

            case DtoType.SportEventSummaryList:
                var sportEventSummaryList = item as EntityList <SportEventSummaryDTO>;
                if (sportEventSummaryList != null)
                {
                    var tasks = sportEventSummaryList.Items.Select(s => SaveCompetitorsFromSportEventAsync(s, culture));
                    await Task.WhenAll(tasks).ConfigureAwait(false);

                    saved = true;
                }
                else
                {
                    LogSavingDtoConflict(id, typeof(SportEventSummaryDTO), item.GetType());
                }
                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;

            case DtoType.AvailableSelections:
                break;

            case DtoType.TournamentInfoList:
                var ts = item as EntityList <TournamentInfoDTO>;
                if (ts != null)
                {
                    var tasks = ts.Items.Select(s => SaveCompetitorsFromSportEventAsync(s, culture));
                    await Task.WhenAll(tasks).ConfigureAwait(false);

                    saved = true;
                }
                else
                {
                    LogSavingDtoConflict(id, typeof(EntityList <TournamentInfoDTO>), item.GetType());
                }
                break;

            default:
                ExecutionLog.LogWarning($"Trying to add unchecked dto type: {dtoType} for id: {id}.");
                break;
            }
            return(saved);
        }