Blazor Server 中未触发事件

Event not firing in Blazor Server

提问人:Alan 提问时间:10/26/2023 最后编辑:Alan 更新时间:10/26/2023 访问量:50

问:

我在 AuthenticationService.cs 中创建了一个事件:

public event Action<string?>? LoginChanged;

在 AuthenticationService 的 Login 方法中,我调用了该事件:

LoginChanged?.Invoke(username); // This line does get hit when the user logs in, but LoginChanged is null

该事件在 BlazorServer 组件中订阅:

UserPartial.razor:

    @inject IAuthenticationService AuthenticationService;

    ...

    protected override void OnInitialized()
    {

        Logger.LogInformation("user.razor initilised 1"); // This does get hit

        AuthenticationService.LoginChanged += (string? name) =>
        {
            Logger.LogInformation("event firing!!!"); // This doesn't get hit

            username = name;

            StateHasChanged();
        };
        Logger.LogInformation("user.razor initilised 2"); // This does get hit

        base.OnInitialized();
    }

AuthenticationService 的依赖项注入在程序 .cs 中设置为单一实例:

builder.Services.AddSingleton<IAuthenticationService, AuthenticationService>();

我的目标是,当 AuthenticationService 的 Login 方法调用 LoginChanged 事件时,将运行 UserPartial.razor 中的事件处理代码。

似乎正在发生的事情是,当 LoginChanged?.调用 Invoke(username),则 LoginEvent 为 null,因此它实际上不会调用。

我尝试了许多附加事件处理程序代码的替代方法,包括在 UserPartial.razor 中创建一个方法,如下所示:

    public void LocalLoginChanged(string? name)
    {
        Logger.LogInformation("event firing!!!");

        username = name;

        StateHasChanged();
    }

并像这样从 OnInitialized 附加它:

AuthenticationService.LoginChanged += new Action<string?>(LocalLoginChanged);

我尝试过其他方法,例如从 OnInitializedAsync 而不是 OnInitialized 调用它,但我尝试的所有内容都有相同的结果。

我错过了什么?为什么 LoginChanged 在调用它时为 null?显然我没有正确设置它,你能给我指出它应该如何设置的方向吗?

我正在使用 .Net Core 7、Blazor Server。

C# .net-core 事件 blazor-server-side

评论

0赞 jeb 10/26/2023
我认为用户登录时该组件不存在。这意味着当您发送事件时,组件将不会侦听。还是顶级组件?但是,有更简单的方法可以将用户名传递给组件。文档非常详细 learn.microsoft.com/en-us/aspnet/core/blazor/security/...
0赞 jeb 10/26/2023
[CascadingParameter] 私有 Task<AuthenticationState>?authenticationState { get; set; } 可能是一个选项
0赞 Alan 10/26/2023
谢谢你的评论!该组件是主布局右上角的用户状态指示器 - 因此当用户登录时它是可见的,因此我(希望)它存在。此外,我可以在组件的 OnInitialized[Async] 中放置一个断点,它会在启动时被命中。

答:

0赞 Ale_Bianco 10/26/2023 #1

如果将 注册为单一实例服务,则事件处理程序可能无法正确连接到组件的特定实例。AuthenticationService

在调用事件之前,请确保该事件不为 null。LoginChanged

public void OnLoginChanged(string? username)
{
    LoginChanged?.Invoke(username);
}

订阅 中的事件。LoginChangedOnInitializedAsync

@inject IAuthenticationService AuthenticationService;

@code {
    private string? username;

    private void LocalLoginChanged(string? name)
    {
        Logger.LogInformation("event firing!!!");
        username = name;
        StateHasChanged();
    }

    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();

        Logger.LogInformation("user.razor initilised 1");

        AuthenticationService.LoginChanged += LocalLoginChanged;

        Logger.LogInformation("user.razor initilised 2");
    }

    // Don't forget to unsubscribe when the component is disposed
    public void Dispose()
    {
        AuthenticationService.LoginChanged -= LocalLoginChanged;
    }
}

评论

0赞 Alan 10/26/2023
谢谢你的回答,但是我以前尝试过的所有这些东西(除了 Dispose 方法),我刚刚再次尝试过以确保。在调用它之前,我已经在检查事件是否为 null(LoginChanged?.调用())。我之前曾尝试将 Singleton 更改为 Scoped,尽管我没有在问题中提到这一点,并且我已经尝试使用 OnInitializedAsync。我不明白的一件事是,如果将 AuthenticationService 设置为单例,怎么可能有另一个实例漂浮在周围?
0赞 SMSTJ 11/28/2023 #2

好的,所以我设法让它工作。我没有选择相同的名字,但我想你会明白的。

所以这是你的服务的代码

    public delegate void AuthDelegate(string name);
    
    public interface IEventTesterAuth
    {
        public event AuthDelegate? OnAuth;
        public void FireEvent(string name);
    }
    
    
    public class EventTesterAuth: IEventTesterAuth
    {
        public event AuthDelegate? OnAuth;
    
        public void FireEvent(string name)
        {
            OnAuth?.Invoke(name);
        }
    }

这是 Blazor 组件中的 c# 代码

    private string _someName;

    protected override void OnInitialized()
    {
        EvTeAu.OnAuth += AuthChanged;


        base.OnInitialized();
    }


    public void FireInvoke()
    {
        EvTeAu.FireEvent("Johnson");
    }

    private async void AuthChanged(string name)
    {
        _someName = name;
        await InvokeAsync(StateHasChanged);
    }

这是测试它的 HTML 部分

<button @onclick="FireInvoke">Invoke</button>
<p role="status">Current name: @_someName</p>

这是注射

@inject IEventTesterAuth EvTeAu

这是在 Main 方法中添加的 Service

builder.Services.AddSingleton<IEventTesterAuth, EventTesterAuth>();

现在,我的主要变化是替换事件中的操作部分以委托,因为这就是我通常的做法。然后,我还确保您的StateHasChanged在InvokeAsync中被调用,因为没有它,页面通常不会更新。您可能仍然应该按照 Ale 的建议在某个时候使用 Dispose。但现在,这可以满足您的需求。