【问题标题】:How to fix 'The current thread is not associated with the renderer's synchronization context'?如何修复“当前线程与渲染器的同步上下文无关”?
【发布时间】:2026-01-09 22:50:02
【问题描述】:

我正在尝试在我的 blazor-server-side 应用程序中更改用于标题的字符串。但我无法让 UI 更新。

我尝试使用 StateHasChanged(),但是没有用,所以我环顾四周,发现在制作的 FlightFinder Demo 上,它有一个 OnChange 事件 Action,所以我正在尝试实现它。

在我尝试刷新浏览器之前它一直有效,然后我遇到了这个错误

System.InvalidOperationException: '当前线程与渲染器的同步上下文无关。在触发渲染或修改渲染期间访问的任何状态时,使用 Invoke() 或 InvokeAsync() 将执行切换到渲染器的同步上下文。'

这就是我所拥有的:

private string _title = "TestSite";
public string Title => _title;

public event Action OnChange;

public void ChangePage(string pageName)
{
   _title = pageName;
   NotifyStateChanged();
}

private void NotifyStateChanged(int navigationType = 0)
{
   OnChange?.Invoke();
}

我所要做的就是调用 ChangePage("some Page Title") 并且它可以工作,除非像我提到的那样我尝试刷新。

我只是想通过另一个组件更改一个组件上的字符串,这听起来并不那么疯狂。如果有更好的方法来制作标题或更改其他组件的内容,我很想听听。

那么,我该怎么做才能确保 m 调用方法在正确的线程上? 还是有其他更有效的方法来更改标题?

提前谢谢你!

【问题讨论】:

    标签: c# asp.net-core blazor .net-core-3.0 blazor-server-side


    【解决方案1】:

    我刚刚实现了一个这样的状态容器并遇到了同样的错误 - 但我的服务需要是单例的。 因此,我在 aspnetcore git 上找到了一个示例,该示例完全按照错误消息的要求执行。 调用 InvokeAsync -- 不是从您的状态容器中调用,而是在您尝试更改剃刀组件的状态时调用。

    https://github.com/dotnet/aspnetcore/blob/321db9d99f84cf7a67d453384292d9339de748d1/src/Components/test/testassets/BasicTestApp/DispatchingComponent.razor

    所以你的状态容器不需要改变,你的组件事件处理程序会做。

    @code{
        protected override void OnInitialized()
        {
             _YourService.OnChange += OnMyChangeHandler;
        }
    
        public void Dispose()
        {
             _YourService.OnChange -= OnMyChangeHandler;
        }
    
        private async void OnMyChangeHandler(object sender, EventArgs e)
        {
            // InvokeAsync is inherited, it syncs the call back to the render thread
            await InvokeAsync(() => {
                DoStuff();
                StateHasChanged());
            }
        }
    }
    

    现在您的服务(如果是单例)可以立即通知您的所有用户!想想我们过去为了做到这一点而必须跳过的所有障碍。

    【讨论】:

    • 它可以与public event Action OnChange; 一起使用吗?
    • @bmiller 非常感谢您对此的回答,大约一个月前它让我摆脱了困境。我在这个帖子中引用了你:*.com/questions/61354233/…
    • 我不确定为什么需要 lambda 表达式。这对我有用:await InvokeAsync(StateHasChanged);
    • 你说得对,不需要 lamda。但是我认为它使正在发生的事情变得更加明显,并且很可能您的“DoStuff()”代码也可能需要在 Invoke 上下文中运行。
    • @nam 您的服务只是像往常一样调用事件; OnChange?.Invoke(this, EventArgs.Empty); 如果你的服务是单例的,每个页面/用户都应该得到这个事件。
    【解决方案2】:

    我早上发布了第一件事,以为我没有时间研究,并认为当有人能够帮助我时,我会抽出时间进行更多研究。虽然我已经花了几天的时间来回讨论这个问题。

    我终于找到了this article,它解释了我正在尝试做的事情被称为状态容器。

    他们说的是我可以将类作为单例注入,这就是我正在做的事情或范围服务。事实证明,我需要做的就是将其更改为范围服务,并且效果很好!

    【讨论】:

    • 在简要阅读了 scoped vs singleton 之后,这很有意义。我仍然会继续提出我的问题,因为我认为 blazor 没有足够的问题和答案,这可能会在某些时候对某人有所帮助。
    • 这对我有用!谢谢
    【解决方案3】:

    不需要复杂的解决方案,如果您通过以下方式更新事件处理程序中的 GUI,Blazor 可以完美运行

    this.InvokeAsync(() => this.StateHasChanged());
    

    【讨论】:

    • 这与 OP 已经得到的答案有什么不同? (没关系,根据他们自我接受的答案,他们显然更喜欢更改服务类型而不是使用InvokeAsync())。我认为这个答案根本不是有用的贡献。它不会对已经存在的信息添加任何内容。
    • 我说的不是 DalTron 的答案。您的 “真正有效的代码” 与 bmiller 答案中发布的代码字面上 相同,除了在另一个答案中的细微差别之外,它们实际上 解释 i> 代码。
    • 这个解决方案不能解决问题
    • 我投了这个答案,因为@Vaicheslav 正好有 666 分。
    最近更新 更多