Blazor Server重复点击按钮无法获取服务原始数据问题咨询
问题:Blazor Server重复查询后DTO属性未恢复原始值
我有一个基于服务后端(无API)的Blazor Server应用,设置了一个按钮,点击后触发按ID查询元素的方法。首次点击按钮运行正常,但重复点击查询时,尽管服务调用已按预期执行(已调试确认流程正确),但集合仍处于修改后的状态(比如示例中Prop1保持false,数据库原始值为true)。
Index.razor代码如下:
<MudNumericField HideSpinButtons="true" @bind-Value="ElementNumber" Label="Element number" Variant="Variant.Text" Required id="element-number-id" OnKeyUp="OnEnterPressed" RequiredError="Cannot be empty" Placeholder="Type a number" /> <MudButton Variant="Variant.Filled" Color="Color.Primary" id="element-search-btn" OnClick="OnSearchElement" Disabled="@_loading_btn">Search</Mudbutton> @code { public IEnumerable<ElemDTO>? Elems { get; set; } public IEnumerable<ElemDTO>? CopyElems { get; set; } public IEnumerable<ElemDTO>? TmpElems { get; set; } public int? ElementNumber {get;set;} = default!; public async void OnSearchElement() { TmpElems = await _service.getElementByNumber(ElementNumber); Elems = CopyElems = FormatElement(TmpElems); await InvokeAsync(() => this.StateHasChanged()); // Elems is used for rendering the collection on a table. } public IEnumerable<ElemDTO>? FormatElement(IEnumerable<ElemDTO>? elems) { // this method modifies the elems collection elems.FirstOrDefault().Prop1 = false; // it overrides some of their properties. return elems; } }
原因分析
这不是外部缓存问题,核心原因是你直接修改了从服务返回的DTO实例引用:
- 如果你的服务使用EF Core这类ORM,默认会开启实体跟踪,上下文会缓存查询到的实例,后续查询会返回同一个对象,之前修改的属性自然会保留。
- 即使服务没有跟踪,只要你没有创建新的DTO副本,修改的就是同一个内存中的对象,再次查询如果返回的是同一引用,属性值不会自动重置。
解决思路
创建DTO副本,避免修改原始实例
在FormatElement方法中,不要直接修改服务返回的DTO,而是创建新的DTO对象并复制属性,仅修改需要调整的字段:public IEnumerable<ElemDTO>? FormatElement(IEnumerable<ElemDTO>? elems) { if (elems == null) return null; return elems.Select(elem => new ElemDTO { // 复制所有原始属性到新实例 Prop1 = false, // 仅修改该属性 Prop2 = elem.Prop2, // 补充其他需要复制的属性... }); }禁用服务端实体跟踪(若使用EF Core)
如果服务基于EF Core实现,查询时添加.AsNoTracking(),确保每次查询都返回全新的实例,避免上下文缓存:// 服务中getElementByNumber方法示例 public async Task<IEnumerable<ElemDTO>> getElementByNumber(int? number) { var entities = await _context.Elems .Where(e => e.Number == number) .AsNoTracking() // 禁用跟踪,每次返回新实例 .ToListAsync(); // 映射到DTO时确保生成新对象 return entities.Select(e => new ElemDTO { Prop1 = e.Prop1, // 其他属性映射... }); }清理长期持有的实例引用
避免将修改后的实例赋值给CopyElems这类长期存在的变量,每次查询后只处理新返回的集合,防止旧实例引用被重复使用。
内容的提问来源于stack exchange,提问作者Mauro Alvarez
相关产品推荐
相关产品推荐

