123 lines
4.6 KiB
C#
123 lines
4.6 KiB
C#
using System;
|
||
using System.Collections.Concurrent;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Threading.Tasks;
|
||
using Microsoft.Extensions.Localization;
|
||
using Sozsoft.Languages.Entities;
|
||
using Volo.Abp.Caching;
|
||
using Volo.Abp.DependencyInjection;
|
||
using Volo.Abp.Domain.Repositories;
|
||
using Volo.Abp.Localization;
|
||
using Volo.Abp.Threading;
|
||
|
||
namespace Sozsoft.Languages.Localization;
|
||
|
||
public class DatabaseResourceLocalizer : IDatabaseResourceLocalizer, ISingletonDependency
|
||
{
|
||
// Her metin aramasında sözlüğün tamamı Redis'ten çekilip çözülüyordu. Süreç içi katman bunu önler.
|
||
// Kısa ömürlüdür: metinlerin bir kısmı ham SQL ile yazılır (olay üretmez) ve diğer API örnekleri ile
|
||
// DbMigrator ayrı süreçtir; onların temizliği buraya en geç bu süre sonunda yansır.
|
||
private static readonly long LocalCacheDurationMs = (long)TimeSpan.FromSeconds(30).TotalMilliseconds;
|
||
|
||
private readonly IRepository<LanguageText, Guid> languageTextRepository;
|
||
private readonly IDistributedCache<LanguageTextCacheItem> cache;
|
||
private readonly ConcurrentDictionary<string, (LanguageTextCacheItem Item, long ExpiresAt)> localCache = new();
|
||
|
||
public DatabaseResourceLocalizer(
|
||
IRepository<LanguageText, Guid> languageTextRepository,
|
||
IDistributedCache<LanguageTextCacheItem> cache)
|
||
{
|
||
this.languageTextRepository = languageTextRepository;
|
||
this.cache = cache;
|
||
}
|
||
|
||
public LocalizedString GetOrNull(LocalizationResourceBase resource, string cultureName, string name)
|
||
{
|
||
var value = GetCacheItem(resource, cultureName)?.Dictionary?.GetOrDefault(name);
|
||
return value == null ? null : new LocalizedString(name, value);
|
||
}
|
||
|
||
/// <summary>Süreç içi katmanı boşaltır; dağıtık önbellek temizlendiğinde ve metin değiştiğinde çağrılır.</summary>
|
||
public void ClearLocalCache() => localCache.Clear();
|
||
|
||
protected LanguageTextCacheItem GetCacheItem(LocalizationResourceBase resource, string cultureName)
|
||
{
|
||
// culture, resource, key
|
||
var key = LanguageTextCacheItem.CalculateCacheKey(resource.ResourceName, cultureName);
|
||
if (TryGetLocal(key, out var local))
|
||
{
|
||
return local;
|
||
}
|
||
|
||
return SetLocal(key, cache.GetOrAdd(key, () => CreateCacheItem(resource, cultureName)));
|
||
}
|
||
|
||
private bool TryGetLocal(string key, out LanguageTextCacheItem item)
|
||
{
|
||
if (localCache.TryGetValue(key, out var entry) && entry.ExpiresAt > Environment.TickCount64)
|
||
{
|
||
item = entry.Item;
|
||
return true;
|
||
}
|
||
|
||
item = null;
|
||
return false;
|
||
}
|
||
|
||
private LanguageTextCacheItem SetLocal(string key, LanguageTextCacheItem item)
|
||
{
|
||
if (item != null)
|
||
{
|
||
localCache[key] = (item, Environment.TickCount64 + LocalCacheDurationMs);
|
||
}
|
||
|
||
return item;
|
||
}
|
||
|
||
public void Fill(LocalizationResourceBase resource, string cultureName, Dictionary<string, LocalizedString> dictionary)
|
||
{
|
||
var item = GetCacheItem(resource, cultureName);
|
||
|
||
foreach (var kvp in item?.Dictionary ?? [])
|
||
{
|
||
dictionary[kvp.Key] = new LocalizedString(kvp.Key, kvp.Value);
|
||
}
|
||
}
|
||
|
||
protected virtual LanguageTextCacheItem CreateCacheItem(LocalizationResourceBase resource, string cultureName)
|
||
{
|
||
var task = languageTextRepository.GetListAsync(a => a.ResourceName == resource.ResourceName && a.CultureName == cultureName);
|
||
var texts = AsyncHelper.RunSync(() => task);
|
||
|
||
return new LanguageTextCacheItem
|
||
{
|
||
Dictionary = texts.ToDictionary(a => a.Key, a => a.Value)
|
||
};
|
||
}
|
||
|
||
public async Task FillAsync(LocalizationResourceBase resource, string cultureName, Dictionary<string, LocalizedString> dictionary)
|
||
{
|
||
var key = LanguageTextCacheItem.CalculateCacheKey(resource.ResourceName, cultureName);
|
||
if (!TryGetLocal(key, out var item))
|
||
{
|
||
item = SetLocal(key, await cache.GetOrAddAsync(key, () => CreateCacheItemAsync(resource, cultureName)));
|
||
}
|
||
|
||
foreach (var kvp in item?.Dictionary ?? [])
|
||
{
|
||
dictionary[kvp.Key] = new LocalizedString(kvp.Key, kvp.Value);
|
||
}
|
||
}
|
||
|
||
protected virtual async Task<LanguageTextCacheItem> CreateCacheItemAsync(LocalizationResourceBase resource, string cultureName)
|
||
{
|
||
var texts = await languageTextRepository.GetListAsync(a => a.ResourceName == resource.ResourceName && a.CultureName == cultureName);
|
||
|
||
return new LanguageTextCacheItem
|
||
{
|
||
Dictionary = texts.ToDictionary(a => a.Key, a => a.Value)
|
||
};
|
||
}
|
||
}
|
||
|