Blazor异步字典服务初始空白、重复加载问题的修复咨询
问题解答
1. 解决初始空白值问题
初始表格显示空白是因为Blazor渲染时,GetIntStrDictVal的异步操作尚未完成,渲染使用了未完成任务的默认结果(空字符串),且操作完成后没有触发组件重新渲染。
解决方案:在页面的OnInitializedAsync中预加载所需字典
在页面初始化时提前加载表格用到的字典(Dict1和Dict2),确保缓存填充完成后再渲染表格,这样GetIntStrDictVal能直接从缓存返回值,无需等待异步操作。
页面代码修改示例:
@inject DictService _ds; <MudTable Items="@List1"> <!-- 原表格内容不变 --> </MudTable> @code { private List<YourModelType> List1; // 替换为你的实际模型类型 protected override async Task OnInitializedAsync() { // 先加载表格依赖的字典 await _ds.GetAllIntStrDict("Dict1"); await _ds.GetAllIntStrDict("Dict2"); // 再加载表格数据 List1 = await LoadTableData(); // 替换为你的数据加载逻辑 } private async Task<List<YourModelType>> LoadTableData() { // 模拟数据加载,替换为实际API调用 await Task.Delay(100); return new List<YourModelType>(); } }
如果无法提前确定所需字典(比如字典依赖表格数据),可在表格数据加载完成后,收集所需字典并加载,最后调用StateHasChanged()触发重新渲染:
protected override async Task OnInitializedAsync() { List1 = await LoadTableData(); // 收集所有需要的字典名称(示例) var requiredDicts = new HashSet<string> { "Dict1", "Dict2" }; foreach (var dictName in requiredDicts) { await _ds.GetAllIntStrDict(dictName); } StateHasChanged(); }
2. 修复字典重复加载问题
原代码中静态Dictionary不是线程安全的,并发场景下多个线程会同时判断缓存不存在,导致重复调用FillIntStrDict和API请求。
解决方案:使用ConcurrentDictionary结合Lazy<Task<>>实现线程安全的异步缓存
通过ConcurrentDictionary存储Lazy<Task<List<...>>>,确保同一个字典仅触发一次API请求,即使多个线程并发调用。
修改后的DictService代码:
public class DictService { private readonly IHttpService _ihttp; private readonly ILogger<DictService> _logger; // 使用ConcurrentDictionary存储Lazy包装的异步任务,确保线程安全 private static readonly ConcurrentDictionary<string, Lazy<Task<List<KeyValuePair<int, string>>>>> _intStrDictCache = new(); public DictService(IHttpService ihttp, ILogger<DictService> logger) { _ihttp = ihttp; _logger = logger; } public async ValueTask<List<KeyValuePair<int, string>>> GetAllIntStrDict(string dictName) { // GetOrAdd确保每个字典仅创建一次Lazy任务 var lazyTask = _intStrDictCache.GetOrAdd(dictName, name => new Lazy<Task<List<KeyValuePair<int, string>>>>(() => { return _ihttp.Get<List<KeyValuePair<int, string>>>($"api/Dicts/IntStr/all/{name}"); })); // 等待任务完成并返回结果 return await lazyTask.Value; } public async ValueTask<string> GetIntStrDictVal(int? key, string dictName) { if (key == null) return string.Empty; var dict = await GetAllIntStrDict(dictName); // 使用SingleOrDefault避免键不存在时抛出异常 var item = dict.SingleOrDefault(x => x.Key == key); return item?.Value ?? string.Empty; } }
关键改进点:
ConcurrentDictionary:线程安全的字典实现,避免并发读写冲突。Lazy<Task<>>:确保异步任务仅被创建和执行一次,即使多个线程同时访问同一个字典。- 移除原
FillIntStrDict方法,将API调用逻辑整合到GetOrAdd的工厂函数中。 - 用
SingleOrDefault替代Single,避免键不存在时抛出异常,增强代码健壮性。
内容的提问来源于stack exchange,提问作者Dorian
相关产品推荐
相关产品推荐

