From 392965bb3af5fa64ca0842d40a3b4d750bedc07a Mon Sep 17 00:00:00 2001 From: Stephan Date: Wed, 11 Apr 2018 15:31:21 +0200 Subject: [PATCH] Faster languages --- .../Cache/FullDataSetRepositoryCachePolicy.cs | 2 +- .../Repositories/ILanguageRepository.cs | 3 + .../Implement/LanguageRepository.cs | 57 ++++++++++++++++--- .../Services/ILocalizationService.cs | 5 ++ .../Services/Implement/LocalizationService.cs | 9 +++ 5 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs b/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs index 8cef4e0296..319d84d41f 100644 --- a/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs +++ b/src/Umbraco.Core/Cache/FullDataSetRepositoryCachePolicy.cs @@ -156,7 +156,7 @@ namespace Umbraco.Core.Cache } // does NOT clone anything, so be nice with the returned values - private IEnumerable GetAllCached(Func> performGetAll) + internal IEnumerable GetAllCached(Func> performGetAll) { // try the cache first var all = Cache.GetCacheItem>(GetEntityTypeCacheKey()); diff --git a/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs index 43f7e9de04..36dd10c3fb 100644 --- a/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/ILanguageRepository.cs @@ -6,5 +6,8 @@ namespace Umbraco.Core.Persistence.Repositories { ILanguage GetByCultureName(string cultureName); ILanguage GetByIsoCode(string isoCode); + + int GetIdByIsoCode(string isoCode); + string GetIsoCodeById(int id); } } diff --git a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs index 00914c92d5..efd6166a9f 100644 --- a/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/Implement/LanguageRepository.cs @@ -18,21 +18,24 @@ namespace Umbraco.Core.Persistence.Repositories.Implement /// internal class LanguageRepository : NPocoRepositoryBase, ILanguageRepository { + private readonly Dictionary _codeIdMap = new Dictionary(); + private readonly Dictionary _idCodeMap = new Dictionary(); + private FullDataSetRepositoryCachePolicy _cachePolicy; + public LanguageRepository(IScopeAccessor scopeAccessor, CacheHelper cache, ILogger logger) : base(scopeAccessor, cache, logger) { } protected override IRepositoryCachePolicy CreateCachePolicy() { - return new FullDataSetRepositoryCachePolicy(GlobalIsolatedCache, ScopeAccessor, GetEntityId, /*expires:*/ false); + return _cachePolicy = new FullDataSetRepositoryCachePolicy(GlobalIsolatedCache, ScopeAccessor, GetEntityId, /*expires:*/ false); } #region Overrides of RepositoryBase protected override ILanguage PerformGet(int id) { - //use the underlying GetAll which will force cache all domains - return GetMany().FirstOrDefault(x => x.Id == id); + throw new NotSupportedException(); // not required since policy is full dataset } protected override IEnumerable PerformGetAll(params int[] ids) @@ -47,8 +50,22 @@ namespace Umbraco.Core.Persistence.Repositories.Implement //even though legacy didn't sort, it should be by id sql.OrderBy(dto => dto.Id); + // get languages + var languages = Database.Fetch(sql).Select(ConvertFromDto).ToList(); - return Database.Fetch(sql).Select(ConvertFromDto); + // initialize the code-id map + lock (_codeIdMap) + { + _codeIdMap.Clear(); + _idCodeMap.Clear(); + foreach (var language in languages) + { + _codeIdMap[language.IsoCode] = language.Id; + _idCodeMap[language.Id] = language.IsoCode; + } + } + + return languages; } protected override IEnumerable PerformGetByQuery(IQuery query) @@ -184,14 +201,40 @@ namespace Umbraco.Core.Persistence.Repositories.Implement public ILanguage GetByCultureName(string cultureName) { - //use the underlying GetAll which will force cache all languages + // use the underlying GetMany which will force cache all languages + // TODO we are cloning ALL in GetMany just to retrieve ONE, this is surely not optimized return GetMany().FirstOrDefault(x => x.CultureName.InvariantEquals(cultureName)); } public ILanguage GetByIsoCode(string isoCode) { - //use the underlying GetAll which will force cache all languages - return GetMany().FirstOrDefault(x => x.IsoCode.InvariantEquals(isoCode)); + _cachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way + var id = GetIdByIsoCode(isoCode); + return Get(id); + } + + // fast way of getting an id for an isoCode - avoiding cloning + // _codeIdMap is rebuilt whenever PerformGetAll runs + public int GetIdByIsoCode(string isoCode) + { + _cachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way + lock (_codeIdMap) + { + if (_codeIdMap.TryGetValue(isoCode, out var id)) return id; + } + throw new ArgumentException($"Code {isoCode} does not correspond to an existing language.", nameof(isoCode)); + } + + // fast way of getting an isoCode for an id - avoiding cloning + // _idCodeMap is rebuilt whenever PerformGetAll runs + public string GetIsoCodeById(int id) + { + _cachePolicy.GetAllCached(PerformGetAll); // ensure cache is populated, in a non-expensive way + lock (_codeIdMap) // yes, we want to lock _codeIdMap + { + if (_idCodeMap.TryGetValue(id, out var isoCode)) return isoCode; + } + throw new ArgumentException($"Id {id} does not correspond to an existing language.", nameof(id),); } } } diff --git a/src/Umbraco.Core/Services/ILocalizationService.cs b/src/Umbraco.Core/Services/ILocalizationService.cs index 4cc66450f8..d61ee5e310 100644 --- a/src/Umbraco.Core/Services/ILocalizationService.cs +++ b/src/Umbraco.Core/Services/ILocalizationService.cs @@ -117,6 +117,11 @@ namespace Umbraco.Core.Services /// ILanguage GetLanguageByIsoCode(string isoCode); + /// + /// Gets a language identifier by its iso code. + /// + int GetLanguageIdByIsoCode(string isoCode); + /// /// Gets all available languages /// diff --git a/src/Umbraco.Core/Services/Implement/LocalizationService.cs b/src/Umbraco.Core/Services/Implement/LocalizationService.cs index 1af053b787..bf01605416 100644 --- a/src/Umbraco.Core/Services/Implement/LocalizationService.cs +++ b/src/Umbraco.Core/Services/Implement/LocalizationService.cs @@ -316,6 +316,15 @@ namespace Umbraco.Core.Services.Implement } } + /// + public int GetLanguageIdByIsoCode(string isoCode) + { + using (ScopeProvider.CreateScope(autoComplete: true)) + { + return _languageRepository.GetIdByIsoCode(isoCode); + } + } + /// /// Gets all available languages ///