Skip to content

Commit 809149f

Browse files
authored
perf(CacheManager): reduce cache key count (#5140)
* refactor: 减少 Cache 缓存项 * refactor: 增加 Lock 对象 * refactor: 增加 GetAllStringsByTypeName 方法 * doc: 增加注释信息 * refactor: 移除不使用的变量 * refactor: 减少缓存键值 * refactor: 更新 GetStringSafelyFromJson 方法 * refactor: 更新 GetAllStringsByType 实现方法 * refactor: 修复缓存时间 * feat: 增加 GetTypeStringsFromResolve 方法 * refactor: 移除 GetAllStringsFromResolve 方法 * refactor: 重构 GetAllStrings 逻辑 * test: 更新单元测试 * test: 精简代码 * test: 精简代码
1 parent 7f19440 commit 809149f

File tree

6 files changed

+106
-56
lines changed

6 files changed

+106
-56
lines changed

src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,6 @@ public partial class BaseLayout : IDisposable
3838
[NotNull]
3939
private string? CancelText { get; set; }
4040

41-
[NotNull]
42-
private string? ThemeTooltip { get; set; }
43-
4441
/// <summary>
4542
/// <inheritdoc/>
4643
/// </summary>
@@ -52,7 +49,6 @@ protected override void OnInitialized()
5249
InstallAppText ??= Localizer[nameof(InstallAppText)];
5350
InstallText ??= Localizer[nameof(InstallText)];
5451
CancelText ??= Localizer[nameof(CancelText)];
55-
ThemeTooltip ??= Localizer[nameof(ThemeTooltip)];
5652

5753
CommitDispatchService.Subscribe(NotifyCommit);
5854
RebootDispatchService.Subscribe(NotifyReboot);

src/BootstrapBlazor/Localization/ILocalizationResolve.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,12 @@ public interface ILocalizationResolve
1818
/// <param name="includeParentCultures"></param>
1919
/// <returns></returns>
2020
IEnumerable<LocalizedString> GetAllStringsByCulture(bool includeParentCultures);
21+
22+
/// <summary>
23+
/// 获得所有文化信息集合
24+
/// </summary>
25+
/// <param name="includeParentCultures"></param>
26+
/// <param name="typeName">类型名称</param>
27+
/// <returns></returns>
28+
IEnumerable<LocalizedString> GetAllStringsByType(string typeName, bool includeParentCultures);
2129
}

src/BootstrapBlazor/Localization/Json/JsonStringLocalizer.cs

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -92,28 +92,40 @@ public override LocalizedString this[string name]
9292
// get string from json localization file
9393
string? GetStringSafelyFromJson(string name)
9494
{
95-
var localizerStrings = CacheManager.GetAllStringsByTypeName(Assembly, typeName);
95+
var localizerStrings = MegerResolveLocalizers(CacheManager.GetAllStringsByTypeName(Assembly, typeName));
9696
return GetValueFromCache(localizerStrings, name);
9797
}
9898
}
9999

100-
private string? GetValueFromCache(IEnumerable<LocalizedString>? localizerStrings, string name)
100+
private List<LocalizedString> MegerResolveLocalizers(IEnumerable<LocalizedString>? localizerStrings)
101+
{
102+
var localizers = new List<LocalizedString>();
103+
var resolveLocalizers = CacheManager.GetTypeStringsFromResolve(typeName);
104+
localizers.AddRange(resolveLocalizers);
105+
106+
if (localizerStrings != null)
107+
{
108+
localizers.AddRange(localizerStrings);
109+
}
110+
return localizers;
111+
}
112+
113+
private readonly HashSet<string> _missingLocalizerCache = [];
114+
115+
private string? GetValueFromCache(List<LocalizedString> localizerStrings, string name)
101116
{
102117
string? ret = null;
103-
var cultureName = CultureInfo.CurrentUICulture.Name;
104-
var cacheKey = $"{nameof(GetValueFromCache)}&name={name}&{Assembly.GetUniqueName()}&type={typeName}&culture={cultureName}";
105-
if (!CacheManager.GetMissingLocalizerByKey(cacheKey))
118+
if (!_missingLocalizerCache.Contains(name))
106119
{
107-
var l = localizerStrings?.FirstOrDefault(i => i.Name == name)
108-
?? CacheManager.GetAllStringsFromResolve().FirstOrDefault(i => i.Name == name);
120+
var l = localizerStrings.Find(i => i.Name == name);
109121
if (l is { ResourceNotFound: false })
110122
{
111123
ret = l.Value;
112124
}
113125
else
114126
{
115127
HandleMissingResourceItem(name);
116-
CacheManager.AddMissingLocalizerByKey(cacheKey, name);
128+
_missingLocalizerCache.Add(name);
117129
}
118130
}
119131
return ret;
@@ -122,9 +134,7 @@ public override LocalizedString this[string name]
122134
private string? GetLocalizerValueFromCache(IStringLocalizer localizer, string name)
123135
{
124136
string? ret = null;
125-
var cultureName = CultureInfo.CurrentUICulture.Name;
126-
var cacheKey = $"{nameof(GetLocalizerValueFromCache)}&name={name}&{Assembly.GetUniqueName()}&type={typeName}&culture={cultureName}";
127-
if (!CacheManager.GetMissingLocalizerByKey(cacheKey))
137+
if (!_missingLocalizerCache.Contains(name))
128138
{
129139
var l = localizer[name];
130140
if (!l.ResourceNotFound)
@@ -134,7 +144,7 @@ public override LocalizedString this[string name]
134144
else
135145
{
136146
HandleMissingResourceItem(name);
137-
CacheManager.AddMissingLocalizerByKey(cacheKey, name);
147+
_missingLocalizerCache.Add(name);
138148
}
139149
}
140150
return ret;
@@ -149,22 +159,28 @@ private void HandleMissingResourceItem(string name)
149159
}
150160
}
151161

162+
private List<LocalizedString>? _allLocalizerdStrings;
163+
152164
/// <summary>
153165
/// 获取当前语言的所有资源信息
154166
/// </summary>
155167
/// <param name="includeParentCultures"></param>
156168
/// <returns></returns>
157169
public override IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
158170
{
159-
var ret = GetAllStringsFromService(includeParentCultures)
160-
?? GetAllStringsFromBase(includeParentCultures)
161-
?? GetAllStringsFromJson(includeParentCultures);
171+
if (_allLocalizerdStrings == null)
172+
{
173+
var items = GetAllStringsFromService()
174+
?? GetAllStringsFromBase()
175+
?? GetAllStringsFromJson();
162176

163-
return ret;
177+
_allLocalizerdStrings = MegerResolveLocalizers(items);
178+
}
179+
return _allLocalizerdStrings;
164180

165181
// 1. 从注入服务中获取所有资源信息
166182
// get all strings from the other inject service
167-
IEnumerable<LocalizedString>? GetAllStringsFromService(bool includeParentCultures)
183+
IEnumerable<LocalizedString>? GetAllStringsFromService()
168184
{
169185
IEnumerable<LocalizedString>? ret = null;
170186
var localizer = Utility.GetStringLocalizerFromService(Assembly, typeName);
@@ -177,7 +193,7 @@ public override IEnumerable<LocalizedString> GetAllStrings(bool includeParentCul
177193

178194
// 2. 从父类 ResourceManagerStringLocalizer 中获取微软格式资源信息
179195
// get all strings from base json localization factory
180-
IEnumerable<LocalizedString>? GetAllStringsFromBase(bool includeParentCultures)
196+
IEnumerable<LocalizedString>? GetAllStringsFromBase()
181197
{
182198
IEnumerable<LocalizedString>? ret = base.GetAllStrings(includeParentCultures);
183199
try
@@ -196,7 +212,6 @@ public override IEnumerable<LocalizedString> GetAllStrings(bool includeParentCul
196212

197213
// 3. 从 Json 文件中获取资源信息
198214
// get all strings from json localization file
199-
IEnumerable<LocalizedString> GetAllStringsFromJson(bool includeParentCultures) => CacheManager.GetAllStringsByTypeName(Assembly, typeName)
200-
?? CacheManager.GetAllStringsFromResolve(includeParentCultures);
215+
IEnumerable<LocalizedString>? GetAllStringsFromJson() => CacheManager.GetAllStringsByTypeName(Assembly, typeName);
201216
}
202217
}

src/BootstrapBlazor/Localization/NullLocalizationResolve.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,18 @@ namespace BootstrapBlazor.Components;
99

1010
internal class NullLocalizationResolve : ILocalizationResolve
1111
{
12+
/// <summary>
13+
/// 获得所有文化信息集合
14+
/// </summary>
15+
/// <param name="includeParentCultures"></param>
16+
/// <returns></returns>
1217
public IEnumerable<LocalizedString> GetAllStringsByCulture(bool includeParentCultures) => [];
18+
19+
/// <summary>
20+
/// 获得指定类型文化信息集合
21+
/// </summary>
22+
/// <param name="typeName">类型名称</param>
23+
/// <param name="includeParentCultures"></param>
24+
/// <returns></returns>
25+
public IEnumerable<LocalizedString> GetAllStringsByType(string typeName, bool includeParentCultures) => [];
1326
}

src/BootstrapBlazor/Services/CacheManager.cs

Lines changed: 27 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public CacheManager(IServiceProvider provider, IMemoryCache memoryCache)
4949
public TItem GetOrCreate<TItem>(object key, Func<ICacheEntry, TItem> factory) => Cache.GetOrCreate(key, entry =>
5050
{
5151
#if DEBUG
52-
entry.SlidingExpiration = TimeSpan.FromSeconds(500000);
52+
entry.SlidingExpiration = TimeSpan.FromSeconds(5);
5353
#endif
5454

5555
if (key is not string)
@@ -181,8 +181,8 @@ private static JsonLocalizationOptions GetJsonLocalizationOption()
181181
/// <summary>
182182
/// 通过 程序集与类型获得 IStringLocalizer 实例
183183
/// </summary>
184-
/// <param name="assembly"></param>
185-
/// <param name="typeName"></param>
184+
/// <param name="assembly">Assembly 程序集实例</param>
185+
/// <param name="typeName">类型名称</param>
186186
/// <returns></returns>
187187
public static IStringLocalizer? GetStringLocalizerFromService(Assembly assembly, string typeName) => assembly.IsDynamic
188188
? null
@@ -205,11 +205,17 @@ private static JsonLocalizationOptions GetJsonLocalizationOption()
205205
/// <summary>
206206
/// 获取指定文化本地化资源集合
207207
/// </summary>
208-
/// <param name="assembly"></param>
209-
/// <param name="typeName"></param>
208+
/// <param name="assembly">Assembly 程序集实例</param>
209+
/// <param name="typeName">类型名称</param>
210210
public static IEnumerable<LocalizedString>? GetAllStringsByTypeName(Assembly assembly, string typeName)
211211
=> GetJsonStringByTypeName(GetJsonLocalizationOption(), assembly, typeName, CultureInfo.CurrentUICulture.Name);
212212

213+
#if NET9_0_OR_GREATER
214+
private static readonly Lock _locker = new();
215+
#else
216+
private static readonly object _locker = new();
217+
#endif
218+
213219
/// <summary>
214220
/// 通过指定程序集获取所有本地化信息键值集合
215221
/// </summary>
@@ -226,60 +232,49 @@ private static JsonLocalizationOptions GetJsonLocalizationOption()
226232
return null;
227233
}
228234

235+
IEnumerable<LocalizedString>? localizedItems = null;
229236
cultureName ??= CultureInfo.CurrentUICulture.Name;
230237
var key = $"{nameof(GetJsonStringByTypeName)}-{assembly.GetUniqueName()}-{cultureName}";
231-
var typeKey = $"{key}-{typeName}";
232238
if (forceLoad)
233239
{
234240
Instance.Cache.Remove(key);
235-
Instance.Cache.Remove(typeKey);
236241
}
237-
return Instance.GetOrCreate(typeKey, _ =>
242+
243+
lock (_locker)
238244
{
239-
var sections = Instance.GetOrCreate(key, _ => option.GetJsonStringFromAssembly(assembly, cultureName));
240-
var items = sections.FirstOrDefault(kv => typeName.Equals(kv.Key, StringComparison.OrdinalIgnoreCase))?
241-
.GetChildren()
242-
.Select(kv =>
245+
localizedItems = Instance.GetOrCreate(key, _ =>
246+
{
247+
var sections = option.GetJsonStringFromAssembly(assembly, cultureName);
248+
var items = sections.SelectMany(section => section.GetChildren().Select(kv =>
243249
{
244250
var value = kv.Value;
245251
if (value == null && option.UseKeyWhenValueIsNull == true)
246252
{
247253
value = kv.Key;
248254
}
249-
return new LocalizedString(kv.Key, value ?? "", false, typeName);
250-
});
255+
return new LocalizedString(kv.Key, value ?? "", false, section.Key);
256+
}));
251257
#if NET8_0_OR_GREATER
252-
return items?.ToFrozenSet();
258+
return items.ToFrozenSet();
253259
#else
254-
return items?.ToHashSet();
260+
return items.ToHashSet();
255261
#endif
256-
});
262+
});
263+
}
264+
return localizedItems.Where(item => item.SearchedLocation!.Equals(typeName, StringComparison.OrdinalIgnoreCase));
257265
}
258266

259267
/// <summary>
260268
/// 通过 ILocalizationResolve 接口实现类获得本地化键值集合
261269
/// </summary>
270+
/// <param name="typeName"></param>
262271
/// <param name="includeParentCultures"></param>
263272
/// <returns></returns>
264-
#if NET8_0_OR_GREATER
265-
public static FrozenSet<LocalizedString> GetAllStringsFromResolve(bool includeParentCultures = true) => Instance.GetOrCreate($"{nameof(GetAllStringsFromResolve)}-{CultureInfo.CurrentUICulture.Name}", entry => Instance.Provider.GetRequiredService<ILocalizationResolve>().GetAllStringsByCulture(includeParentCultures).ToFrozenSet());
266-
#else
267-
public static HashSet<LocalizedString> GetAllStringsFromResolve(bool includeParentCultures = true) => Instance.GetOrCreate($"{nameof(GetAllStringsFromResolve)}-{CultureInfo.CurrentUICulture.Name}", entry => Instance.Provider.GetRequiredService<ILocalizationResolve>().GetAllStringsByCulture(includeParentCultures).ToHashSet());
268-
#endif
273+
public static IEnumerable<LocalizedString> GetTypeStringsFromResolve(string typeName, bool includeParentCultures = true) => Instance.Provider.GetRequiredService<ILocalizationResolve>().GetAllStringsByType(typeName, includeParentCultures);
269274

270275
/// <summary>
271-
/// 查询缺失本地化资源项目
272276
/// </summary>
273-
/// <param name="key"></param>
274277
/// <returns></returns>
275-
public static bool GetMissingLocalizerByKey(string key) => Instance.TryGetValue(key, out string? _);
276-
277-
/// <summary>
278-
/// 添加缺失本地化资源项目
279-
/// </summary>
280-
/// <param name="key"></param>
281-
/// <param name="name"></param>
282-
public static void AddMissingLocalizerByKey(string key, string name) => Instance.GetOrCreate(key, entry => name);
283278
#endregion
284279

285280
#region DisplayName

test/UnitTest/Localization/JsonStringLocalizerTest.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,18 @@ public void GetAllStrings_Ok(bool include)
114114
Assert.NotEmpty(items);
115115
}
116116

117+
[Fact]
118+
public void GetAllStrings_Dynamic()
119+
{
120+
var dynamicType = EmitHelper.CreateTypeByName("test_type", new InternalTableColumn[] { new("Name", typeof(string)) });
121+
Assert.NotNull(dynamicType);
122+
123+
var factory = Context.Services.GetRequiredService<IStringLocalizerFactory>();
124+
var localizer = factory.Create(dynamicType);
125+
var items = localizer.GetAllStrings();
126+
Assert.Empty(items);
127+
}
128+
117129
[Fact]
118130
public void GetAllStrings_FromInject()
119131
{
@@ -199,6 +211,9 @@ public void GetAllStrings_FromJson()
199211
var items = localizer.GetAllStrings(false);
200212
Assert.Equal("姓名", items.First(i => i.Name == "Name").Value);
201213
Assert.DoesNotContain("Test-JsonName", items.Select(i => i.Name));
214+
215+
var resolve = provider.GetRequiredService<ILocalizationResolve>();
216+
Assert.Empty(resolve.GetAllStringsByCulture(true));
202217
}
203218

204219
[Fact]
@@ -211,6 +226,8 @@ public void GetAllStrings_FromResolver()
211226

212227
var provider = sc.BuildServiceProvider();
213228
var localizer = provider.GetRequiredService<IStringLocalizer<Foo>>();
229+
230+
// test-localizer-name 通过 MockLocalizationResolve 获得
214231
Assert.Equal("name", localizer["test-localizer-name"]);
215232
Assert.Equal("test-name", localizer["test-name"]);
216233
}
@@ -403,6 +420,12 @@ internal class MockLocalizationResolve : ILocalizationResolve
403420
new("test-localizer-name", "name"),
404421
new("test-localizer-age", "age")
405422
};
423+
424+
public IEnumerable<LocalizedString> GetAllStringsByType(string typeName, bool includeParentCultures) => new LocalizedString[]
425+
{
426+
new("test-localizer-name", "name"),
427+
new("test-localizer-age", "age")
428+
};
406429
}
407430

408431
internal class MockLocalizationMissingItemHandler : ILocalizationMissingItemHandler

0 commit comments

Comments
 (0)