Small groundwork done, switching to another task due to blocker

This commit is contained in:
NillasKA
2026-02-25 12:46:07 +01:00
parent 75f60aa184
commit 4e618cba04
3 changed files with 59 additions and 29 deletions
@@ -12,7 +12,7 @@ public interface ILanguageRepository : IReadWriteQueryRepository<int, ILanguage>
/// </summary>
/// <param name="isoCode">The ISO code of the language.</param>
/// <returns>The language if found; otherwise, <c>null</c>.</returns>
ILanguage? GetByIsoCode(string isoCode);
Task<ILanguage?> GetByIsoCodeAsync(string isoCode);
/// <summary>
/// Gets a language identifier from its ISO code.
@@ -20,7 +20,7 @@ public interface ILanguageRepository : IReadWriteQueryRepository<int, ILanguage>
/// <remarks>
/// <para>This can be optimized and bypass all deep cloning.</para>
/// </remarks>
int? GetIdByIsoCode(string? isoCode, bool throwOnNotFound = true);
Task<int?> GetIdByIsoCodeAsync(string? isoCode, bool throwOnNotFound = true);
/// <summary>
/// Gets a language ISO code from its identifier.
@@ -28,7 +28,7 @@ public interface ILanguageRepository : IReadWriteQueryRepository<int, ILanguage>
/// <remarks>
/// <para>This can be optimized and bypass all deep cloning.</para>
/// </remarks>
string? GetIsoCodeById(int? id, bool throwOnNotFound = true);
Task<string?> GetIsoCodeByIdAsync(int? id, bool throwOnNotFound = true);
/// <summary>
/// Gets the default language ISO code.
@@ -36,7 +36,7 @@ public interface ILanguageRepository : IReadWriteQueryRepository<int, ILanguage>
/// <remarks>
/// <para>This can be optimized and bypass all deep cloning.</para>
/// </remarks>
string GetDefaultIsoCode();
Task<string> GetDefaultIsoCodeAsync();
/// <summary>
/// Gets the default language identifier.
@@ -44,7 +44,7 @@ public interface ILanguageRepository : IReadWriteQueryRepository<int, ILanguage>
/// <remarks>
/// <para>This can be optimized and bypass all deep cloning.</para>
/// </remarks>
int? GetDefaultId();
Task<int?> GetDefaultIdAsync();
/// <summary>
/// Gets multiple language ISO codes from the provided Ids.
@@ -52,5 +52,5 @@ public interface ILanguageRepository : IReadWriteQueryRepository<int, ILanguage>
/// <param name="ids">The language Ids.</param>
/// <param name="throwOnNotFound">Indicates whether to throw an exception if the provided Id is not found as a language.</param>
/// <returns></returns>
string[] GetIsoCodesByIds(ICollection<int> ids, bool throwOnNotFound = true);
Task<string[]> GetIsoCodesByIdsAsync(ICollection<int> ids, bool throwOnNotFound = true);
}
@@ -2,6 +2,7 @@
// See LICENSE for more details.
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Scoping.EFCore;
@@ -20,6 +21,8 @@ public abstract class AsyncRepositoryCachePolicyBase<TEntity, TId> : IAsyncRepos
private readonly IRepositoryCacheVersionService _cacheVersionService;
private readonly ICacheSyncService _cacheSyncService;
protected string EntityTypeCacheKey { get; } = RepositoryCacheKeys.GetKey<TEntity>();
protected AsyncRepositoryCachePolicyBase(
IAppPolicyCache globalCache,
IScopeAccessor scopeAccessor,
@@ -97,4 +100,21 @@ public abstract class AsyncRepositoryCachePolicyBase<TEntity, TId> : IAsyncRepos
/// Registers a change in the cache.
/// </summary>
protected async Task RegisterCacheChangeAsync() => await _cacheVersionService.SetCacheUpdatedAsync<TEntity>();
protected string GetEntityCacheKey(int id) => EntityTypeCacheKey + id;
protected string GetEntityCacheKey(TId? id)
{
if (EqualityComparer<TId>.Default.Equals(id, default))
{
return string.Empty;
}
if (typeof(TId).IsValueType)
{
return EntityTypeCacheKey + id;
}
return EntityTypeCacheKey + id?.ToString()?.ToUpperInvariant();
}
}
@@ -6,9 +6,11 @@ using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Persistence.Querying;
using Umbraco.Cms.Core.Persistence.Repositories;
using Umbraco.Cms.Infrastructure.Persistence.Dtos;
using Umbraco.Cms.Infrastructure.Persistence.EFCore;
using Umbraco.Cms.Infrastructure.Persistence.EFCore.Scoping;
using Umbraco.Cms.Infrastructure.Persistence.Factories;
using Umbraco.Cms.Infrastructure.Persistence.Querying;
using Umbraco.Cms.Infrastructure.Scoping;
using Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement.EFCore;
using Umbraco.Extensions;
namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
@@ -16,7 +18,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Repositories.Implement;
/// <summary>
/// Represents a repository for doing CRUD operations for <see cref="Language" />
/// </summary>
internal sealed class LanguageRepository : EntityRepositoryBase<int, ILanguage>, ILanguageRepository
internal sealed class LanguageRepository : AsyncEntityRepositoryBase<int, ILanguage>, ILanguageRepository
{
// We need to lock this dictionary every time we do an operation on it as the languageRepository is registered as a unique implementation
// It is used to quickly get isoCodes by Id, or the reverse by avoiding (deep)cloning dtos
@@ -25,7 +27,7 @@ internal sealed class LanguageRepository : EntityRepositoryBase<int, ILanguage>,
private readonly Dictionary<int, string> _idCodeMap = new();
public LanguageRepository(
IScopeAccessor scopeAccessor,
IEFCoreScopeAccessor<UmbracoDbContext> scopeAccessor,
AppCaches cache,
ILogger<LanguageRepository> logger,
IRepositoryCacheVersionService repositoryCacheVersionService,
@@ -42,22 +44,22 @@ internal sealed class LanguageRepository : EntityRepositoryBase<int, ILanguage>,
private FullDataSetRepositoryCachePolicy<ILanguage, int>? TypedCachePolicy =>
CachePolicy as FullDataSetRepositoryCachePolicy<ILanguage, int>;
public ILanguage? GetByIsoCode(string isoCode)
public async Task<ILanguage?> GetByIsoCodeAsync(string isoCode)
{
EnsureCacheIsPopulated();
await EnsureCacheIsPopulatedAsync();
var id = GetIdByIsoCode(isoCode, false);
return id.HasValue ? Get(id.Value) : null;
var id = await GetIdByIsoCodeAsync(isoCode, false);
return id.HasValue ? GetAsync(id.Value) : null;
}
public int? GetIdByIsoCode(string? isoCode, bool throwOnNotFound = true)
public async Task<int?> GetIdByIsoCodeAsync(string? isoCode, bool throwOnNotFound = true)
{
if (isoCode == null)
{
return null;
}
EnsureCacheIsPopulated();
await EnsureCacheIsPopulatedAsync();
lock (_codeIdMap)
{
@@ -75,14 +77,14 @@ internal sealed class LanguageRepository : EntityRepositoryBase<int, ILanguage>,
return null;
}
public string? GetIsoCodeById(int? id, bool throwOnNotFound = true)
public async Task<string?> GetIsoCodeByIdAsync(int? id, bool throwOnNotFound = true)
{
if (id == null)
{
return null;
}
EnsureCacheIsPopulated();
await EnsureCacheIsPopulatedAsync();
lock (_codeIdMap)
{
@@ -101,7 +103,7 @@ internal sealed class LanguageRepository : EntityRepositoryBase<int, ILanguage>,
}
// multi implementation of GetIsoCodeById
public string[] GetIsoCodesByIds(ICollection<int> ids, bool throwOnNotFound = true)
public async Task<string[]> GetIsoCodesByIdsAsync(ICollection<int> ids, bool throwOnNotFound = true)
{
var isoCodes = new string[ids.Count];
@@ -110,7 +112,7 @@ internal sealed class LanguageRepository : EntityRepositoryBase<int, ILanguage>,
return isoCodes;
}
EnsureCacheIsPopulated();
await EnsureCacheIsPopulatedAsync();
lock (_codeIdMap)
@@ -132,14 +134,22 @@ internal sealed class LanguageRepository : EntityRepositoryBase<int, ILanguage>,
return isoCodes;
}
public string GetDefaultIsoCode() => GetDefault().IsoCode;
public async Task<string> GetDefaultIsoCodeAsync()
{
ILanguage defaultLanguage = await GetDefaultAsync();
return defaultLanguage.IsoCode;
}
public int? GetDefaultId() => GetDefault().Id;
public async Task<int?> GetDefaultIdAsync()
{
ILanguage defaultLanguage = await GetDefaultAsync();
return defaultLanguage.Id;
}
protected override IRepositoryCachePolicy<ILanguage, int> CreateCachePolicy() =>
protected override IAsyncRepositoryCachePolicy<ILanguage, int> CreateCachePolicy() =>
new FullDataSetRepositoryCachePolicy<ILanguage, int>(GlobalIsolatedCache, ScopeAccessor, RepositoryCacheVersionService, CacheSyncService, GetEntityId, /*expires:*/ false);
private ILanguage ConvertFromDto(LanguageDto dto)
private async Task<ILanguage> ConvertFromDtoAsync(LanguageDto dto)
{
lock (_codeIdMap)
{
@@ -154,14 +164,14 @@ internal sealed class LanguageRepository : EntityRepositoryBase<int, ILanguage>,
}
// do NOT leak that language, it's not deep-cloned!
private ILanguage GetDefault()
private async Task<ILanguage> GetDefaultAsync()
{
// get all cached
var languages =
(TypedCachePolicy
?.GetAllCached(
PerformGetAll) // Try to get all cached non-cloned if using the correct cache policy (not the case in unit tests)
?? CachePolicy.GetAll(Array.Empty<int>(), PerformGetAll)).ToList();
?? await CachePolicy.GetAllAsync(Array.Empty<int>(), PerformGetAll)).ToList();
ILanguage? language = languages.FirstOrDefault(x => x.IsDefault);
if (language != null)
@@ -188,9 +198,9 @@ internal sealed class LanguageRepository : EntityRepositoryBase<int, ILanguage>,
#region Overrides of RepositoryBase<int,Language>
protected override ILanguage? PerformGet(int id) => PerformGetAll([id]).FirstOrDefault();
protected override async Task<ILanguage?> PerformGetAsync(int id) => PerformGetAll([id]).FirstOrDefault();
protected override IEnumerable<ILanguage> PerformGetAll(params int[]? ids)
protected override async Task<IEnumerable<ILanguage>> PerformGetAll(params int[]? ids)
{
Sql<ISqlContext> sql = GetBaseQuery(false).Where<LanguageDto>(x => x.Id > 0);
if (ids?.Any() ?? false)
@@ -221,7 +231,7 @@ internal sealed class LanguageRepository : EntityRepositoryBase<int, ILanguage>,
}
}
var languages = languageDtos.Select(ConvertFromDto).OrderBy(x => x.Id).ToList();
var languages = languageDtos.Select(ConvertFromDtoAsync).OrderBy(x => x.Id).ToList();
return languages;
}
@@ -420,7 +430,7 @@ internal sealed class LanguageRepository : EntityRepositoryBase<int, ILanguage>,
}
}
private void EnsureCacheIsPopulated()
private async Task EnsureCacheIsPopulatedAsync()
{
// ensure cache is populated, in a non-expensive way
if (TypedCachePolicy != null)