【问题标题】:How can I invoke method in CircuitHandler of Blazor server-side?如何在 Blazor 服务器端的 CircuitHandler 中调用方法?
【发布时间】:2020-05-16 21:06:39
【问题描述】:

我正在通过 Blazor 服务器端制作聊天室应用程序。我想展示每个用户的在线状态。

我问了一个问题,关于如何在关闭How can I get the event while page close in blazor server-side? 中的页面时获取事件

现在看来CircuitHandler 是最好的选择。

当用户关闭页面时,我想在数据库中设置一个用户状态从在线到离线。而且,每个用户的主键都暂时存储在index.razor中。

但是现在OnCircuitClosedAsync(Circuit, CancellationToken)运行后,我不知道如何调用方法来实现这一点(我无法获取Blazor前端的变量或调用前端的Blazor方法)。

PS: 下面是后端的代码:

using Microsoft.AspNetCore.Components.Server.Circuits;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;


namespace BlazorCircuitHandler.Services
{
    public class CircuitHandlerService : CircuitHandler
    {
        public ConcurrentDictionary<string, Circuit> Circuits { get; set; }

        public CircuitHandlerService()
        {
            Circuits = new ConcurrentDictionary<string, Circuit>();
        }

        public override Task OnCircuitOpenedAsync(Circuit circuit, CancellationToken cancellationToken)
        {
            Circuits[circuit.Id] = circuit;
            return base.OnCircuitOpenedAsync(circuit, cancellationToken);
        }

        public override Task OnCircuitClosedAsync(Circuit circuit, CancellationToken cancellationToken)
        {
            Circuit circuitRemoved;
            Circuits.TryRemove(circuit.Id, out circuitRemoved);
            return base.OnCircuitClosedAsync(circuit, cancellationToken);
        }

        public override Task OnConnectionDownAsync(Circuit circuit, CancellationToken cancellationToken)
        {
            return base.OnConnectionDownAsync(circuit, cancellationToken);
        }

        public override Task OnConnectionUpAsync(Circuit circuit, CancellationToken cancellationToken)
        {
            return base.OnConnectionUpAsync(circuit, cancellationToken);
        }
    }
}

这里是前端:

@page "/"

@using Microsoft.AspNetCore.Components.Server.Circuits
@inject CircuitHandler CircuitHandlerService

<h1>Hello, world!</h1>

Welcome to your new app.

<p>
    Number of Circuits: @((CircuitHandlerService as BlazorCircuitHandler.Services.CircuitHandlerService).Circuits.Count)

    <ul>
        @foreach (var circuit in (CircuitHandlerService as BlazorCircuitHandler.Services.CircuitHandlerService).Circuits)
        {
            <li>@circuit.Key</li>            
        }
    </ul>
    @{ 
        var PrimaryKey = "abcdefg";
    }
</p>

你能帮帮我吗?谢谢。

【问题讨论】:

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


    【解决方案1】:

    我猜这应该可以工作;)

    CircuitHandlerService.cs

    using Microsoft.AspNetCore.Components.Server.Circuits;
    using System;
    using System.Collections.Concurrent;
    using System.Threading;
    using System.Threading.Tasks;
    
    
    namespace BlazorCircuitHandler.Services
    {
        public class CircuitHandlerService : CircuitHandler
        {
            public ConcurrentDictionary<string, Circuit> Circuits { get; 
                set; }
            public event EventHandler CircuitsChanged;
    
            protected virtual void OnCircuitsChanged()
                 => CircuitsChanged?.Invoke(this, EventArgs.Empty);
    
            public CircuitHandlerService()
            {
                 Circuits = new ConcurrentDictionary<string, Circuit>();
            }
    
            public override Task OnCircuitOpenedAsync(Circuit circuit, 
                                 CancellationToken cancellationToken)
           {
                 Circuits[circuit.Id] = circuit;
                 OnCircuitsChanged();
                 return base.OnCircuitOpenedAsync(circuit, 
                                       cancellationToken);
           }
    
           public override Task OnCircuitClosedAsync(Circuit circuit, 
                     CancellationToken cancellationToken)
          {
               Circuit circuitRemoved;
               Circuits.TryRemove(circuit.Id, out circuitRemoved);
               OnCircuitsChanged();
               return base.OnCircuitClosedAsync(circuit, 
                                 cancellationToken);
          }
    
          public override Task OnConnectionDownAsync(Circuit circuit, 
                                CancellationToken cancellationToken)
          {
              return base.OnConnectionDownAsync(circuit, 
                               cancellationToken);
          }
    
          public override Task OnConnectionUpAsync(Circuit circuit, 
                              CancellationToken cancellationToken)
          {
              return base.OnConnectionUpAsync(circuit, cancellationToken);
          }
       }
     }
    

    用法

    @page "/"
    
    @using Microsoft.AspNetCore.Components.Server.Circuits
    @using BlazorCircuitHandler.Services
    
    @inject CircuitHandler circuitHandler
    @implements IDisposable
    
    
    
    <h1>Hello, world!</h1>
    
    Welcome to your new app.
    
    <p>
     Number of Circuits: @((circuitHandler as 
     BlazorCircuitHandler.Services.CircuitHandlerService).Circuits.Count)
     <ul>
        @foreach (var circuit in (circuitHandler as 
         BlazorCircuitHandler.Services.CircuitHandlerService).Circuits)
        {
            <li>@circuit.Key</li>
        }
     </ul>
    </p>
    
    @code {
       protected override void OnInitialized()
       {
           // Subscribe to the event handler
        (circuitHandler as CircuitHandlerService).CircuitsChanged += 
             HandleCircuitsChanged;
        
        }
    
     public void Dispose()
     {
        // Unsubscribe the event handler when the component is disposed
        (circuitHandler as CircuitHandlerService).CircuitsChanged -= 
          HandleCircuitsChanged;
       
     }
    
     public void HandleCircuitsChanged(object sender, EventArgs args)
     {
        // notify the component that its state has changed 
        // Important: You must use InvokeAsync
        InvokeAsync(() => StateHasChanged());
     }
    }
    

    Startup.cs

    public void ConfigureServices(IServiceCollection services)
            {
                services.AddRazorPages();
                services.AddServerSideBlazor();
    
                services.AddSingleton<CircuitHandler>(new CircuitHandlerService());
            }
    

    注意:要验证它是否正常运行,请运行该应用程序。然后打开另外两个选项卡。现在,关闭您打开的第一个选项卡(从左到右),然后关闭第二个。注意显示有源电路的数量...

    【讨论】:

    • 现在效果很好。但是,我仍然很好奇为什么 Dispose 方法在页面刚刚加载时调用。
    • 我将不得不调查它...但是,这只是一个猜测,我必须稍后验证它:这是因为组件标签助手的渲染模式属性设置为_Host.cshtml 文件中的“ServerPrerendered”。您可以将其设置为“Server”,如下所示: 。现在我相信这次不会调用 Dispose 方法,因为预渲染已停用。
    • 这个解决方案从今天开始有效吗?当我尝试注入它时,我得到'没有注册的 CircuitHandlerService 类型的服务......(当然,它在 Startup.cs 中注册为 Singleton)
    • 这个方法看起来很适合确定用户何时与服务器断开连接。我想知道确定“哪个”用户与服务器断开连接是否也有用。例如,是否可以将 circuitKey 与给定用户关联起来?在我的应用程序中,我想知道给定用户何时断开连接,因此我可以将它们从我的数据库中删除。
    • @rfcdejong,我在这里发布了我的解决方案:stackoverflow.com/a/66581485/10787774。希望它能回答你的问题。
    【解决方案2】:

    您可以在电路处理程序中公开一个事件:

    public class CircuitHandlerService : CircuitHandler
    {
        public event EventHandler CircuitsChanged;
    
        protected virtual void OnCircuitsChanged()
            => CircuitsChanged?.Invoke(this, EventArgs.Empty);
    
        public override Task OnCircuitOpenedAsync(Circuit circuit, CancellationToken cancellationToken)
        {
            Circuits[circuit.Id] = circuit;
            OnCircuitsChanged();
            return Task.CompletedTask;
        }
    
        public override Task OnCircuitClosedAsync(Circuit circuit, CancellationToken cancellationToken)
        {
            Circuits.TryRemove(circuit.Id, out var circuitRemoved);
            OnCircuitsChanged();
            return Task.CompletedTask;
        }
    }
    

    然后,在您的 Razor 组件中,您可以订阅该事件并根据需要刷新 UI:

    @implements IDisposable
    @inject CircuitHandler circuitHandler
    
    <ul>
        @foreach (var circuit in Circuits)
        {
            <li>@circuit.Key</li>
        }
    </ul>
    
    @code {
        private IDictionary<string, Circuit> Circuits
            => (circuitHandler as CircuitHandlerService).Circuits;
    
        protected override void OnInitialized()
        {
            // register event handler
            (circuitHandler as CircuitHandlerService).CircuitsChanged += HandleCircuitsChanged;
        }
    
        public void Dispose()
        {
            // unregister the event handler when the component is destroyed
            (circuitHandler as CircuitHandlerService).CircuitsChanged -= HandleCircuitsChanged;
        }
    
        public void HandleCircuitsChanged(object sender, EventArgs args)
        {
            // notify the UI that the state has changed
            StateHasChanged();
        }
    }
    

    【讨论】:

    • 它有效。但是,我在 OnInitialized/Dispose/HandleCircuitsChanged 中添加了一个断点。在页面刚加载时,它会先调用 OnInitialized 的断点,然后调用 Dispose。我不知道它会在刚加载时调用 Dispose 的断点。
    • 而且,HandleCircuitsChanged 的​​断点似乎永远不会被调用,无论我打开一个新页面还是关闭一个现有页面。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-29
    • 2021-10-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多