diff --git a/src/BootstrapBlazor.Server/Extensions/CacheManagerExtensions.cs b/src/BootstrapBlazor.Server/Extensions/CacheManagerExtensions.cs deleted file mode 100644 index 5d07de3f5..000000000 --- a/src/BootstrapBlazor.Server/Extensions/CacheManagerExtensions.cs +++ /dev/null @@ -1,38 +0,0 @@ -// 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(argo@live.ca) Website: https://www.blazor.zone - -using Microsoft.Extensions.Caching.Memory; -using System.Globalization; - -namespace BootstrapBlazor.Server.Extensions; - -/// -/// CacheManager 扩展类 -/// -internal static class CacheManagerExtensions -{ - /// - /// 获得 指定代码文件当前文化设置的本地化资源集合 - /// - /// - /// - /// - /// - public static IEnumerable GetLocalizedStrings(this ICacheManager cache, string typeName, JsonLocalizationOptions options) - { - var key = $"Snippet-{CultureInfo.CurrentUICulture.Name}-{nameof(GetLocalizedStrings)}-{typeName}"; - return cache.GetOrCreate(key, entry => - { - var type = typeName.Replace('\\', '.'); - return Utility.GetJsonStringByTypeName(options, typeof(CodeSnippetService).Assembly, $"BootstrapBlazor.Server.Components.Samples.{type}"); - }); - } - - public static Task GetContentFromFileAsync(this ICacheManager cache, string fileName, Func> factory) - { - var key = $"Snippet-{CultureInfo.CurrentUICulture.Name}-{nameof(GetContentFromFileAsync)}-{fileName}"; - return cache.GetOrCreateAsync(key, entry => factory(entry)); - } -} diff --git a/src/BootstrapBlazor.Server/Services/CodeSnippetService.cs b/src/BootstrapBlazor.Server/Services/CodeSnippetService.cs index d563d9577..4024bb12d 100644 --- a/src/BootstrapBlazor.Server/Services/CodeSnippetService.cs +++ b/src/BootstrapBlazor.Server/Services/CodeSnippetService.cs @@ -4,53 +4,23 @@ // Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone using Microsoft.Extensions.Options; -using System.Collections.Frozen; +using System.Globalization; namespace BootstrapBlazor.Server.Services; -class CodeSnippetService +/// +/// 构造方法 +/// +/// +/// +/// +/// +class CodeSnippetService( + IHttpClientFactory factory, + ICacheManager cacheManager, + IOptions options, + IOptions localizerOptions) { - private IHttpClientFactory Factory { get; set; } - - private string ServerUrl { get; set; } - - private string SourceCodePath { get; set; } - - private FrozenDictionary SourceCodes { get; set; } - - private bool IsDevelopment { get; } - - private string ContentRootPath { get; } - - private ICacheManager CacheManager { get; set; } - - private JsonLocalizationOptions LocalizerOptions { get; } - - /// - /// 构造方法 - /// - /// - /// - /// - /// - public CodeSnippetService( - IHttpClientFactory factory, - ICacheManager cacheManager, - IOptionsMonitor options, - IOptionsMonitor localizerOptions) - { - LocalizerOptions = localizerOptions.CurrentValue; - - CacheManager = cacheManager; - Factory = factory; - - IsDevelopment = options.CurrentValue.IsDevelopment; - ContentRootPath = options.CurrentValue.ContentRootPath; - ServerUrl = options.CurrentValue.ServerUrl; - SourceCodes = options.CurrentValue.SourceCodes; - SourceCodePath = options.CurrentValue.SourceCodePath; - } - /// /// 获得示例源码方法 /// @@ -63,14 +33,16 @@ class CodeSnippetService // codeFile = ajax.razor.cs var segs = codeFile.Split('.'); var key = segs[0]; - var typeName = SourceCodes.TryGetValue(key.ToLowerInvariant(), out var value) ? value : string.Empty; + var typeName = options.Value.SourceCodes.TryGetValue(key.ToLowerInvariant(), out var value) ? value : string.Empty; if (!string.IsNullOrEmpty(typeName)) { var fileName = codeFile.Replace(key, typeName); content = await GetFileContentAsync(fileName); // 源码修正 - CacheManager.GetLocalizedStrings(typeName, LocalizerOptions).ToList().ForEach(l => content = ReplacePayload(content, l)); + var type = typeName.Replace('\\', '.'); + Utility.GetJsonStringByTypeName(localizerOptions.Value, typeof(CodeSnippetService).Assembly, $"BootstrapBlazor.Server.Components.Samples.{type}").ToList() + .ForEach(l => content = ReplacePayload(content, l)); content = ReplaceSymbols(content); content = RemoveBlockStatement(content, "@inject IStringLocalizer<"); } @@ -93,15 +65,20 @@ class CodeSnippetService string? payload; if (OperatingSystem.IsBrowser()) { - var client = Factory.CreateClient(); + var client = factory.CreateClient(); client.Timeout = TimeSpan.FromSeconds(5); - client.BaseAddress = new Uri($"{ServerUrl}/api/"); + client.BaseAddress = new Uri($"{options.Value.ServerUrl}/api/"); payload = await client.GetStringAsync($"Code?fileName=BootstrapBlazor.Server/Components/Samples/{fileName}"); } else { // 读取硬盘文件 - payload = await CacheManager.GetContentFromFileAsync(fileName, _ => ReadFileAsync(fileName)); + var key = $"{nameof(GetFileContentAsync)}-{fileName}-{CultureInfo.CurrentUICulture.Name}"; + payload = await cacheManager.GetOrCreateAsync(key, entry => + { + entry.SlidingExpiration = TimeSpan.FromMinutes(10); + return ReadFileAsync(fileName); + }); } return payload; } @@ -109,9 +86,9 @@ class CodeSnippetService private async Task ReadFileAsync(string fileName) { string? payload; - var file = IsDevelopment - ? $"{ContentRootPath}\\..\\BootstrapBlazor.Server\\Components\\Samples\\{fileName}" - : $"{SourceCodePath}BootstrapBlazor.Server\\Components\\Samples\\{fileName}"; + var file = options.Value.IsDevelopment + ? $"{options.Value.ContentRootPath}\\..\\BootstrapBlazor.Server\\Components\\Samples\\{fileName}" + : $"{options.Value.SourceCodePath}BootstrapBlazor.Server\\Components\\Samples\\{fileName}"; if (!OperatingSystem.IsWindows()) { file = file.Replace('\\', '/'); diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index 6c128a84e..426c4720b 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@ - 9.2.9-beta01 + 9.2.9-beta02 diff --git a/src/BootstrapBlazor/Services/CacheManager.cs b/src/BootstrapBlazor/Services/CacheManager.cs index a250b4f68..67eb3929f 100644 --- a/src/BootstrapBlazor/Services/CacheManager.cs +++ b/src/BootstrapBlazor/Services/CacheManager.cs @@ -36,7 +36,6 @@ internal class CacheManager : ICacheManager /// public CacheManager(IServiceProvider provider, IMemoryCache memoryCache) { - // 为了避免依赖导致的报错,构造函数避免使用其他服务 Provider = provider; Cache = memoryCache; Instance = this; @@ -47,11 +46,13 @@ internal class CacheManager : ICacheManager /// public TItem GetOrCreate(object key, Func factory) => Cache.GetOrCreate(key, entry => { - if (key is not string) + var item = factory(entry); + + if (entry.SlidingExpiration == null && entry.AbsoluteExpiration == null && entry.Priority != CacheItemPriority.NeverRemove) { entry.SetSlidingExpiration(TimeSpan.FromMinutes(5)); } - return factory(entry); + return item; })!; /// @@ -90,6 +91,10 @@ internal class CacheManager : ICacheManager /// public void Clear(object? key) { + if (key is "BootstrapBlazor_StartTime") + { + return; + } if (key is not null) { Cache.Remove(key); @@ -97,9 +102,6 @@ internal class CacheManager : ICacheManager else if (Cache is MemoryCache c) { c.Compact(100); - - var dtm = GetStartTime(); - SetStartTime(dtm); } } @@ -113,7 +115,11 @@ internal class CacheManager : ICacheManager /// private void SetStartTime(DateTimeOffset startDateTimeOffset) { - GetOrCreate("BootstrapBlazor_StartTime", _ => startDateTimeOffset); + GetOrCreate("BootstrapBlazor_StartTime", entry => + { + entry.Priority = CacheItemPriority.NeverRemove; + return startDateTimeOffset; + }); } /// @@ -470,7 +476,15 @@ internal class CacheManager : ICacheManager { var type = model.GetType(); var cacheKey = ($"Lambda-Get-{type.GetUniqueTypeName()}", typeof(TModel), fieldName, typeof(TResult)); - var invoker = Instance.GetOrCreate(cacheKey, entry => LambdaExtensions.GetPropertyValueLambda(model, fieldName).Compile()); + var invoker = Instance.GetOrCreate(cacheKey, entry => + { + if (type.Assembly.IsDynamic) + { + entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(10)); + } + + return LambdaExtensions.GetPropertyValueLambda(model, fieldName).Compile(); + }); return invoker(model); } } @@ -487,15 +501,17 @@ internal class CacheManager : ICacheManager d.SetValue(fieldName, value); } else - { - SetValue(); - } - - void SetValue() { var type = model.GetType(); var cacheKey = ($"Lambda-Set-{type.GetUniqueTypeName()}", typeof(TModel), fieldName, typeof(TValue)); - var invoker = Instance.GetOrCreate(cacheKey, entry => LambdaExtensions.SetPropertyValueLambda(model, fieldName).Compile()); + var invoker = Instance.GetOrCreate(cacheKey, entry => + { + if (type.Assembly.IsDynamic) + { + entry.SetAbsoluteExpiration(TimeSpan.FromSeconds(10)); + } + return LambdaExtensions.SetPropertyValueLambda(model, fieldName).Compile(); + }); invoker(model, value); } } diff --git a/test/UnitTest/Services/CacheManagerTest.cs b/test/UnitTest/Services/CacheManagerTest.cs index f090132b3..798022563 100644 --- a/test/UnitTest/Services/CacheManagerTest.cs +++ b/test/UnitTest/Services/CacheManagerTest.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. // Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone +using Microsoft.Extensions.Caching.Memory; + namespace UnitTest.Services; public class CacheManagerTest : BootstrapBlazorTestBase @@ -14,19 +16,32 @@ public class CacheManagerTest : BootstrapBlazorTestBase var v = Cache.GetStartTime(); Assert.Equal(DateTimeOffset.MinValue, v); - Cache.GetOrCreate("BootstrapBlazor_StartTime", entry => - { - return 10; - }); - var v1 = Cache.GetStartTime(); - Assert.Equal(DateTimeOffset.MinValue, v); - - Cache.Clear("BootstrapBlazor_StartTime"); Cache.SetStartTime(); - Assert.True(DateTime.Now > Cache.GetStartTime()); + Assert.Equal(1, Cache.Count); + Cache.Clear("BootstrapBlazor_StartTime"); + Assert.Equal(1, Cache.Count); + Cache.Clear(); Assert.Equal(1, Cache.Count); - Assert.Single(Cache.Keys); + Assert.NotEqual(DateTimeOffset.MinValue, Cache.GetStartTime()); + } + + [Fact] + public void GetStartTime_Number() + { + var context = new TestContext(); + context.Services.AddBootstrapBlazor(); + var cache = context.Services.GetRequiredService(); + + var v = cache.GetOrCreate("BootstrapBlazor_StartTime", entry => + { + return 1; + }); + Assert.Equal(1, v); + + var v2 = cache.GetStartTime(); + Assert.Equal(DateTimeOffset.MinValue, v2); + Assert.Empty(Cache.Keys); } [Fact] @@ -90,6 +105,7 @@ public class CacheManagerTest : BootstrapBlazorTestBase int GetOrCreate(string key) => Cache.GetOrCreate(key, entry => { + entry.SlidingExpiration = TimeSpan.FromSeconds(1); val++; return val; }); @@ -108,6 +124,7 @@ public class CacheManagerTest : BootstrapBlazorTestBase int GetOrCreate(string key) => Cache.GetOrCreate(key, entry => { + entry.AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(1); val++; return val; });