Skip to content

Commit 895170d

Browse files
authored
feat(ICacheManager): add TryGetCacheEntry method (#5216)
* doc: 增加到期时间列 * test: 增加 benmark 测试功能 * feat: 增加 TryGetCacheEntry 方法 * doc: 增加过期时间 * test: 更新单元测试 * test: 更新单元测试
1 parent 01b3e1d commit 895170d

File tree

15 files changed

+291
-1
lines changed

15 files changed

+291
-1
lines changed

BootstrapBlazor.sln

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "cert", "cert", "{C075C6C8-B
7474
scripts\linux\cert\www.blazor.zone.key = scripts\linux\cert\www.blazor.zone.key
7575
EndProjectSection
7676
EndProject
77+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{9BAF50BE-141D-4429-93A9-942F373D1F68}"
78+
EndProject
79+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTest.Benchmarks", "tools\Benchmarks\UnitTest.Benchmarks.csproj", "{3E6D8D0E-5A36-4CFD-8612-7D64E3FFE7B1}"
80+
EndProject
7781
Global
7882
GlobalSection(SolutionConfigurationPlatforms) = preSolution
7983
Debug|Any CPU = Debug|Any CPU
@@ -104,6 +108,10 @@ Global
104108
{D8AEAFE7-10AF-4A5B-BC67-FE740A2CA1DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
105109
{D8AEAFE7-10AF-4A5B-BC67-FE740A2CA1DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
106110
{D8AEAFE7-10AF-4A5B-BC67-FE740A2CA1DF}.Release|Any CPU.Build.0 = Release|Any CPU
111+
{3E6D8D0E-5A36-4CFD-8612-7D64E3FFE7B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
112+
{3E6D8D0E-5A36-4CFD-8612-7D64E3FFE7B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
113+
{3E6D8D0E-5A36-4CFD-8612-7D64E3FFE7B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
114+
{3E6D8D0E-5A36-4CFD-8612-7D64E3FFE7B1}.Release|Any CPU.Build.0 = Release|Any CPU
107115
EndGlobalSection
108116
GlobalSection(SolutionProperties) = preSolution
109117
HideSolutionNode = FALSE
@@ -119,6 +127,7 @@ Global
119127
{6D73FED6-0086-460B-84FA-1FA78176BF59} = {7C1D79F1-87BC-42C1-BD5A-CDE4044AC1BD}
120128
{D8AEAFE7-10AF-4A5B-BC67-FE740A2CA1DF} = {7C1D79F1-87BC-42C1-BD5A-CDE4044AC1BD}
121129
{C075C6C8-B9CB-4AC0-9BDF-B2002B4AB99C} = {EA765165-0542-41C8-93F2-85787FEDEDFF}
130+
{3E6D8D0E-5A36-4CFD-8612-7D64E3FFE7B1} = {9BAF50BE-141D-4429-93A9-942F373D1F68}
122131
EndGlobalSection
123132
GlobalSection(ExtensibilityGlobals) = postSolution
124133
SolutionGuid = {0DCB0756-34FA-4FD0-AE1D-D3F08B5B3A6B}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@ExpirationTime
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
5+
6+
using Microsoft.Extensions.Caching.Memory;
7+
8+
namespace BootstrapBlazor.Server.Components.Pages;
9+
10+
/// <summary>
11+
/// CacaheExpiration 组件
12+
/// </summary>
13+
public partial class CacaheExpiration
14+
{
15+
[Inject, NotNull]
16+
private ICacheManager? CacheManager { get; set; }
17+
18+
/// <summary>
19+
/// 获得/设置 <see cref="TableColumnContext{TItem, TValue}"/> 实例
20+
/// </summary>
21+
[Parameter, NotNull]
22+
public object? Key { get; set; }
23+
24+
private string? ExpirationTime { get; set; }
25+
26+
/// <summary>
27+
/// <inheritdoc/>
28+
/// </summary>
29+
/// <returns></returns>
30+
protected override async Task OnParametersSetAsync()
31+
{
32+
await base.OnParametersSetAsync();
33+
34+
await GetCacheEntryExpiration();
35+
}
36+
37+
private async Task GetCacheEntryExpiration()
38+
{
39+
ExpirationTime = "loading ...";
40+
await Task.Yield();
41+
42+
if (CacheManager.TryGetCacheEntry(Key, out ICacheEntry? entry))
43+
{
44+
if (entry.Priority == CacheItemPriority.NeverRemove)
45+
{
46+
ExpirationTime = "Never Remove";
47+
}
48+
else if (entry.SlidingExpiration.HasValue)
49+
{
50+
ExpirationTime = $"Sliding: {entry.SlidingExpiration.Value}";
51+
}
52+
else if (entry.AbsoluteExpiration.HasValue)
53+
{
54+
ExpirationTime = $"Absolute: {entry.AbsoluteExpiration.Value}";
55+
}
56+
else if (entry.ExpirationTokens.Count != 0)
57+
{
58+
ExpirationTime = $"Token: {entry.ExpirationTokens.Count}";
59+
}
60+
}
61+
}
62+
}

src/BootstrapBlazor.Server/Components/Pages/CacheList.razor

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
@GetValue(v.Row)
1919
</Template>
2020
</TableTemplateColumn>
21+
<TableTemplateColumn Text="@Localizer["CacheListExpiration"]" Width="160">
22+
<Template Context="v">
23+
<CacaheExpiration Key="v.Row"></CacaheExpiration>
24+
</Template>
25+
</TableTemplateColumn>
2126
<TableTemplateColumn Text="@Localizer["CacheListAction"]" Width="80">
2227
<Template Context="v">
2328
<Button Size="Size.ExtraSmall" Color="Color.Danger" OnClick="() => OnDelete(v.Row)" Icon="fa-solid fa-xmark" Text="@Localizer["CacheListDelete"]"></Button>

src/BootstrapBlazor.Server/Components/Pages/CacheList.razor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ private void OnRefresh()
4949

5050
private void UpdateCacheList()
5151
{
52-
_cacheList = CacheManager.Keys.OrderBy(i => i.ToString()).ToList();
52+
_cacheList = [.. CacheManager.Keys.OrderBy(i => i.ToString())];
5353
}
5454

5555
private string GetValue(object key)

src/BootstrapBlazor.Server/Locales/en-US.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6910,6 +6910,7 @@
69106910
"CacheListIntro": "Manage the component library internal cache through the <code>ICacheManager</code> interface method",
69116911
"CacheListKey": "Key",
69126912
"CacheListValue": "Value",
6913+
"CacheListExpiration": "Expiration",
69136914
"CacheListAction": "Action",
69146915
"CacheListRefresh": "Refresh",
69156916
"CacheListDelete": "Delete",

src/BootstrapBlazor.Server/Locales/zh-CN.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6910,6 +6910,7 @@
69106910
"CacheListIntro": "通过 <code>ICacheManager</code> 接口方法管理组件库内部缓存",
69116911
"CacheListKey": "",
69126912
"CacheListValue": "",
6913+
"CacheListExpiration": "到期时间",
69136914
"CacheListAction": "操作",
69146915
"CacheListRefresh": "刷新",
69156916
"CacheListDelete": "删除",

src/BootstrapBlazor/Services/CacheManager.cs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using System.Reflection;
1313

1414
#if NET8_0_OR_GREATER
15+
using System.Runtime.CompilerServices;
1516
using System.Collections.Frozen;
1617
#endif
1718

@@ -172,6 +173,53 @@ public IEnumerable<object> Keys
172173
return keys;
173174
}
174175
}
176+
177+
private object? _coherentStateInstance = null;
178+
179+
private MethodInfo? _allValuesMethodInfo = null;
180+
181+
/// <summary>
182+
/// <inheritdoc/>
183+
/// </summary>
184+
/// <param name="key"></param>
185+
/// <param name="entry"></param>
186+
/// <returns></returns>
187+
public bool TryGetCacheEntry(object? key, [NotNullWhen(true)] out ICacheEntry? entry)
188+
{
189+
entry = null;
190+
if (key == null)
191+
{
192+
return false;
193+
}
194+
195+
if (Cache is MemoryCache cache)
196+
{
197+
var values = GetAllValues(cache);
198+
entry = values.Find(e => e.Key == key);
199+
}
200+
return entry != null;
201+
}
202+
203+
private static object GetCoherentState(MemoryCache cache)
204+
{
205+
var fieldInfo = cache.GetType().GetField("_coherentState", BindingFlags.Instance | BindingFlags.NonPublic)!;
206+
return fieldInfo.GetValue(cache)!;
207+
}
208+
209+
private static MethodInfo GetAllValuesMethodInfo(object coherentStateInstance) => coherentStateInstance.GetType().GetMethod("GetAllValues", BindingFlags.Instance | BindingFlags.Public)!;
210+
211+
private List<ICacheEntry> GetAllValues(MemoryCache cache)
212+
{
213+
_coherentStateInstance ??= GetCoherentState(cache);
214+
_allValuesMethodInfo ??= GetAllValuesMethodInfo(_coherentStateInstance);
215+
216+
var ret = new List<ICacheEntry>();
217+
if (_allValuesMethodInfo.Invoke(_coherentStateInstance, null) is IEnumerable<ICacheEntry> values)
218+
{
219+
ret.AddRange(values);
220+
}
221+
return ret;
222+
}
175223
#endif
176224

177225
#region Count

src/BootstrapBlazor/Services/ICacheManager.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,13 @@ public interface ICacheManager
6666
/// 获得 缓存键集合
6767
/// </summary>
6868
IEnumerable<object> Keys { get; }
69+
70+
/// <summary>
71+
/// 通过指定 key 获取缓存项 <see cref="ICacheEntry"/> 实例
72+
/// </summary>
73+
/// <param name="key"></param>
74+
/// <param name="entry"></param>
75+
/// <returns></returns>
76+
bool TryGetCacheEntry(object? key, [NotNullWhen(true)] out ICacheEntry? entry);
6977
#endif
7078
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the Apache 2.0 License
3+
// See the LICENSE file in the project root for more information.
4+
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone
5+
6+
using System.Runtime.CompilerServices;
7+
8+
namespace UnitTest.Performance;
9+
10+
public class UnsafeAccessorTest
11+
{
12+
[Fact]
13+
public void GetField_Ok()
14+
{
15+
var dummy = new Dummy();
16+
dummy.SetName("test");
17+
Assert.Equal("test", GetNameField(dummy));
18+
}
19+
20+
[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_name")]
21+
static extern ref string GetNameField(Dummy @this);
22+
23+
private class Dummy
24+
{
25+
private string? _name;
26+
27+
/// <summary>
28+
///
29+
/// </summary>
30+
/// <returns></returns>
31+
public string? GetName()
32+
{
33+
return _name;
34+
}
35+
36+
public void SetName(string? name)
37+
{
38+
_name = name;
39+
}
40+
}
41+
}

0 commit comments

Comments
 (0)