Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@ namespace BootstrapBlazor.Server.Components.Pages;
/// </summary>
public partial class CacaheExpiration
{
[Inject, NotNull]
private ICacheManager? CacheManager { get; set; }

/// <summary>
/// 获得/设置 <see cref="TableColumnContext{TItem, TValue}"/> 实例
/// 获得/设置 <see cref="ICacheEntry"/> 实例
/// </summary>
[Parameter, NotNull]
public object? Key { get; set; }
public object? Context { get; set; }

private string? ExpirationTime { get; set; }

Expand All @@ -39,24 +36,6 @@ private async Task GetCacheEntryExpiration()
ExpirationTime = "loading ...";
await Task.Yield();

if (CacheManager.TryGetCacheEntry(Key, out ICacheEntry? entry))
{
if (entry.Priority == CacheItemPriority.NeverRemove)
{
ExpirationTime = "Never Remove";
}
else if (entry.SlidingExpiration.HasValue)
{
ExpirationTime = $"Sliding: {entry.SlidingExpiration.Value}";
}
else if (entry.AbsoluteExpiration.HasValue)
{
ExpirationTime = $"Absolute: {entry.AbsoluteExpiration.Value}";
}
else if (entry.ExpirationTokens.Count != 0)
{
ExpirationTime = $"Token: {entry.ExpirationTokens.Count}";
}
}
ExpirationTime = Context is ICacheEntry entry ? entry.GetExpiration() : "-";
}
}
6 changes: 3 additions & 3 deletions src/BootstrapBlazor.Server/Components/Pages/CacheList.razor
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@
<TableColumns>
<TableTemplateColumn Text="@Localizer["CacheListKey"]" TextWrap="true">
<Template Context="v">
@v.Row.ToString()
@GetKey(v.Row)
</Template>
</TableTemplateColumn>
<TableTemplateColumn Text="@Localizer["CacheListValue"]" TextWrap="true" Width="220">
<Template Context="v">
@GetValue(v.Row)
</Template>
</TableTemplateColumn>
<TableTemplateColumn Text="@Localizer["CacheListExpiration"]" Width="160">
<TableTemplateColumn Text="@Localizer["CacheListExpiration"]" Width="180">
<Template Context="v">
<CacaheExpiration Key="v.Row"></CacaheExpiration>
<CacaheExpiration Context="v.Row"></CacaheExpiration>
</Template>
</TableTemplateColumn>
<TableTemplateColumn Text="@Localizer["CacheListAction"]" Width="80">
Expand Down
43 changes: 22 additions & 21 deletions src/BootstrapBlazor.Server/Components/Pages/CacheList.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ public partial class CacheList
/// <summary>
/// <inheritdoc/>
/// </summary>
protected override void OnInitialized()
protected override async Task OnParametersSetAsync()
{
base.OnInitialized();
await base.OnParametersSetAsync();

await Task.Yield();
UpdateCacheList();
}

Expand All @@ -49,30 +51,29 @@ private void OnRefresh()

private void UpdateCacheList()
{
_cacheList = [.. CacheManager.Keys.OrderBy(i => i.ToString())];
}

private string GetValue(object key)
{
string ret = "-";
if (CacheManager.TryGetValue(key, out object? value))
_cacheList = CacheManager.Keys.OrderBy(i => i.ToString()).Select(key =>
{
if (value is string stringValue)
ICacheEntry? entry = null;
if (CacheManager.TryGetCacheEntry(key, out var val))
{
ret = stringValue;
return ret;
entry = val;
}
return (object)entry!;
}).ToList();
}

if (value is IEnumerable)
{
ret = $"{LambdaExtensions.ElementCount(value)}";
}
else
{
ret = value?.ToString() ?? "-";
}
private static string GetKey(object data) => data is ICacheEntry entry ? entry.Key.ToString()! : "-";

private static string GetValue(object data) => data is ICacheEntry entry ? GetCacheEntryValue(entry) : "-";

private static string GetCacheEntryValue(ICacheEntry entry)
{
var value = entry.Value;
if (value is string stringValue)
{
return stringValue;
}

return ret;
return value is IEnumerable ? $"{LambdaExtensions.ElementCount(value)}" : value?.ToString() ?? "-";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ partial class AutoFills
[NotNull]
private Foo Model3 { get; set; } = new();

private static string OnGetDisplayText(Foo foo) => foo.Name ?? "";
private static string? OnGetDisplayText(Foo? foo) => foo?.Name;

[NotNull]
private IEnumerable<Foo>? Items1 { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@
<div class="col-12 col-sm-6">
<BootstrapInputGroup>
<BootstrapInputGroupLabel DisplayText="AutoFill" />
<AutoFill Items="AufoFillItems" IsLikeMatch="true" OnGetDisplayText="@(foo => foo.Name)">
<AutoFill Items="AufoFillItems" IsLikeMatch="true" OnGetDisplayText="@(foo => foo?.Name)">
<ItemTemplate>
<div class="d-flex">
<div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ protected override void OnInitialized()

private readonly IEnumerable<SelectedItem> Items = new SelectedItem[]
{
new SelectedItem("", "请选择 ..."),
new SelectedItem("Beijing", "北京"),
new SelectedItem("Shanghai", "上海")
new("", "请选择 ..."),
new("Beijing", "北京"),
new("Shanghai", "上海")
};
private string? GroupFormClassString => CssBuilder.Default("row g-3").AddClass("form-inline", FormRowType == RowType.Inline).Build();

Expand Down
52 changes: 52 additions & 0 deletions src/BootstrapBlazor.Server/Extensions/ICacheEntryExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone

using Microsoft.Extensions.Caching.Memory;
using System.Reflection;

namespace BootstrapBlazor.Server.Extensions;

/// <summary>
/// <see cref="ICacheEntry"/> 扩展方法
/// </summary>
public static class ICacheEntryExtensions
{
/// <summary>
/// 获取缓存项过期时间
/// </summary>
/// <param name="entry"></param>
/// <returns></returns>
public static string GetExpiration(this ICacheEntry entry)
{
string? ret;
if (entry.Priority == CacheItemPriority.NeverRemove)
{
ret = "Never Remove";
}
else if (entry.SlidingExpiration.HasValue)
{
ret = $"Sliding: {entry.GetSlidingLeftTime().TotalSeconds:###}/{entry.SlidingExpiration.Value.TotalSeconds}";
}
else if (entry.AbsoluteExpiration.HasValue)
{
ret = $"Absolute: {entry.AbsoluteExpiration.Value}";
}
else if (entry.ExpirationTokens.Count != 0)
{
ret = $"Token: {entry.ExpirationTokens.Count}";
}
else
{
ret = "Not Set";
}
return ret;
}

private static TimeSpan GetSlidingLeftTime(this ICacheEntry entry)
{
var lastAccessed = entry.GetLastAccessed();
return lastAccessed == null ? TimeSpan.Zero : entry.SlidingExpiration!.Value - (DateTime.UtcNow - lastAccessed.Value);
}
}
2 changes: 1 addition & 1 deletion src/BootstrapBlazor/Components/AutoFill/AutoFill.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public partial class AutoFill<TValue>
/// </summary>
[Parameter]
[NotNull]
public Func<TValue, string?>? OnGetDisplayText { get; set; }
public Func<TValue?, string?>? OnGetDisplayText { get; set; }

/// <summary>
/// 图标
Expand Down
43 changes: 43 additions & 0 deletions src/BootstrapBlazor/Extensions/ICacheEntryExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone

using Microsoft.Extensions.Caching.Memory;
using System.Reflection;

namespace BootstrapBlazor.Components;

/// <summary>
/// <see cref="ICacheEntry"/> 扩展方法
/// </summary>
public static class ICacheEntryExtensions
{
/// <summary>
/// 获得缓存项 <see cref="ICacheEntry"/> 最后访问时间
/// </summary>
/// <param name="entry"></param>
/// <param name="force"></param>
/// <returns></returns>
public static DateTime? GetLastAccessed(this ICacheEntry entry, bool force = false)
{
if (force)
{
_lastAccessedProperty = null;
}
_lastAccessedProperty ??= entry.GetType().GetProperty("LastAccessed", BindingFlags.Instance | BindingFlags.NonPublic);

DateTime? ret = null;
if (_lastAccessedProperty != null)
{
var v = _lastAccessedProperty.GetValue(entry);
if (v is DateTime val)
{
ret = val;
}
}
return ret;
}

private static PropertyInfo? _lastAccessedProperty = null;
}
16 changes: 6 additions & 10 deletions src/BootstrapBlazor/Services/CacheManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ public IEnumerable<object> Keys

private MethodInfo? _allValuesMethodInfo = null;

private static readonly FieldInfo _coherentStateFieldInfo = typeof(MemoryCache).GetField("_coherentState", BindingFlags.Instance | BindingFlags.NonPublic)!;

private static MethodInfo GetAllValuesMethodInfo(Type type) => type.GetMethod("GetAllValues", BindingFlags.Instance | BindingFlags.Public)!;

/// <summary>
/// <inheritdoc/>
/// </summary>
Expand All @@ -200,18 +204,10 @@ public bool TryGetCacheEntry(object? key, [NotNullWhen(true)] out ICacheEntry? e
return entry != null;
}

private static object GetCoherentState(MemoryCache cache)
{
var fieldInfo = cache.GetType().GetField("_coherentState", BindingFlags.Instance | BindingFlags.NonPublic)!;
return fieldInfo.GetValue(cache)!;
}

private static MethodInfo GetAllValuesMethodInfo(object coherentStateInstance) => coherentStateInstance.GetType().GetMethod("GetAllValues", BindingFlags.Instance | BindingFlags.Public)!;

private List<ICacheEntry> GetAllValues(MemoryCache cache)
{
_coherentStateInstance ??= GetCoherentState(cache);
_allValuesMethodInfo ??= GetAllValuesMethodInfo(_coherentStateInstance);
_coherentStateInstance = _coherentStateFieldInfo.GetValue(cache)!;
_allValuesMethodInfo ??= GetAllValuesMethodInfo(_coherentStateInstance.GetType());

var ret = new List<ICacheEntry>();
if (_allValuesMethodInfo.Invoke(_coherentStateInstance, null) is IEnumerable<ICacheEntry> values)
Expand Down
60 changes: 60 additions & 0 deletions test/UnitTest/Extensions/ICacheEntryExtensionsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License
// See the LICENSE file in the project root for more information.
// Maintainer: Argo Zhang([email protected]) Website: https://www.blazor.zone

using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Primitives;

namespace UnitTest.Extensions;

public class ICacheEntryExtensionsTest : BootstrapBlazorTestBase
{
[Fact]
public void GetLastAccessed_Ok()
{
Cache.GetOrCreate("test_01", entry =>
{
return 1;
});

Assert.True(Cache.TryGetCacheEntry("test_01", out var entry));
var v = entry.GetLastAccessed(true);
Assert.NotNull(v);
}

[Fact]
public void GetLastAccessed_Null()
{
var mock = new MockCacheEntry();
var v = mock.GetLastAccessed(true);
Assert.Null(v);
}

class MockCacheEntry : ICacheEntry
{
public object Key { get; }
public object? Value { get; set; }
public DateTimeOffset? AbsoluteExpiration { get; set; }
public TimeSpan? AbsoluteExpirationRelativeToNow { get; set; }
public TimeSpan? SlidingExpiration { get; set; }
public IList<IChangeToken> ExpirationTokens { get; }
public IList<PostEvictionCallbackRegistration> PostEvictionCallbacks { get; }
public CacheItemPriority Priority { get; set; }
public long? Size { get; set; }

private int LastAccessed { get; set; }

public MockCacheEntry()
{
Key = "_test";
ExpirationTokens = [];
PostEvictionCallbacks = [];
}

public void Dispose()
{
throw new NotImplementedException();
}
}
}
Loading