关闭浏览器选项卡上的Blazor服务器端Fluxor Dispatch操作



第一篇SO帖子,如果我的问题没有充分整合,请告诉我!

用例:用户打开浏览器;付费设备";按钮我调度了一个PayOnDevice操作,它将UI更新为加载状态。我有一个HandlePayOnDevice〔Effect〕方法,它会拾取该操作并异步启动设备进行支付。当用户将卡插入设备时,支付成功/失败,异步方法解析并将UI更新为成功/失败。然而,如果浏览器在异步方法解析之前关闭,我希望让设备完成并告诉另一个服务设备付款成功或失败。

问题:从技术上讲,我能够通过重写FluxorComponent的虚拟Dispose(bool disposition(方法来调度一个操作";浏览器关闭";。然后,当原始设备异步方法解析时,我可以检查IState,看看浏览器是否关闭,以知道是更新UI还是用结果更新其他系统。这是覆盖Dispose方法:

protected override void Dispose(bool disposing)
{
Dispatcher.Dispatch(new BrowserClosedAction());
base.Dispose(disposing);
}

在这个dispose方法中调用Dispatch的问题是,呈现逻辑的某些东西中断了,因为组件被处理了,所以抛出了一个错误(但IState仍然在更新,所以我的Effect方法仍然可以判断是更新UI还是更新不同的服务(:

Unhandled exception in circuit '2y97VfGYbGWD9xJIhtsbLOfj9PsQChpDlqBrGQdVXTQ'.
System.ObjectDisposedException: Cannot process pending renders after the renderer has been disposed.
Object name: 'Renderer'.
at Microsoft.AspNetCore.Components.RenderTree.Renderer.ProcessPendingRender()
at Microsoft.AspNetCore.Components.Server.Circuits.RemoteRenderer.ProcessPendingRender()
at Microsoft.AspNetCore.Components.RenderTree.Renderer.AddToRenderQueue(Int32 componentId, RenderFragment renderFragment)
at Microsoft.AspNetCore.Components.ComponentBase.StateHasChanged()
at Microsoft.AspNetCore.Components.Rendering.RendererSynchronizationContextDispatcher.InvokeAsync(Action workItem)
at Microsoft.AspNetCore.Components.ComponentBase.InvokeAsync(Action workItem)
at Fluxor.Blazor.Web.Components.FluxorComponent.<OnInitialized>b__8_0(IState _)
at Fluxor.StateSubscriber.<>c__DisplayClass2_1.<Subscribe>b__1(Object s, EventArgs a)
at Fluxor.Feature`1.TriggerStateChangedCallbacks(TState newState)   at Fluxor.Feature`1.set_State(TState value)
at Fluxor.Store.DequeueActions()
at Fluxor.Store.Dispatch(Object action)
at MyComponent.Dispose(Boolean disposing) in C:MyComponentPathMyComponent.razor:line XXX
at Fluxor.Blazor.Web.Components.FluxorComponent.Dispose()        
at Microsoft.AspNetCore.Components.Rendering.ComponentState.Dispose()
at Microsoft.AspNetCore.Components.RenderTree.Renderer.Dispose(Boolean disposing)

从技术上讲,即使出现了这个错误,我仍然可以做我需要做的事情,但由于这个错误(我希望这是可以避免的!(,我不认为我应该走这条路,我想知道是否有其他方法可以实现这一点。我认为可能有一种方法可以创建一个Scoped CircuitHandler来调度操作,但我不知道如何将IState/Dispatcher注入到该电路处理程序中。此电路处理程序失败:

public class BrowserClosedHandler : CircuitHandler
{
BrowserClosedHandler(IState<S.AppState> appState, IDispatcher dispatcher)
{
AppState = appState;
Dispatcher = dispatcher;
} 
private IState<S.AppState> AppState;
private IDispatcher Dispatcher;
public override Task OnConnectionDownAsync(Circuit circuit, 
CancellationToken cancellationToken)
{
Dispatcher.Dispatch(new BrowserClosedAction());
return Task.CompletedTask;
}
}
...
// SERVICES SETUP
services.AddScoped<CircuitHandler, BrowserClosedHandler>();
services.AddFluxor(o => o
.ScanAssemblies(typeof(Program).Assembly)
.UseRouting()
);

这导致启动时出现错误:

The application failed to start correctly
System.AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Components.Server.Circuits.CircuitHandler Lifetime: Scoped ImplementationType: ECC.Startup+BrowserClosedHandler': A suitable constructor for type 'ECC.Startup+BrowserClosedHandler' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.)
---> System.InvalidOperationException: Error while validating the service descriptor 'ServiceType: Microsoft.AspNetCore.Components.Server.Circuits.CircuitHandler Lifetime: Scoped ImplementationType: ECC.Startup+BrowserClosedHandler': A suitable constructor for type 'ECC.Startup+BrowserClosedHandler' could not be located. Ensure the type 
is concrete and services are registered for all parameters of a public constructor.
---> System.InvalidOperationException: A suitable constructor for type 'ECC.Startup+BrowserClosedHandler' could not be located. Ensure 
the type is concrete and services are registered for all parameters 
of a public constructor.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(ResultCache lifetime, Type serviceType, Type implementationType, CallSiteChain callSiteChain)        
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, Int32 slot)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.GetCallSite(ServiceDescriptor serviceDescriptor, CallSiteChain callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.ValidateService(ServiceDescriptor descriptor)        
--- End of inner exception stack trace ---
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.ValidateService(ServiceDescriptor descriptor)        
at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(IEnumerable`1 serviceDescriptors, ServiceProviderOptions options)  
--- End of inner exception stack trace ---
at Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(IEnumerable`1 serviceDescriptors, ServiceProviderOptions options)  
at Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection services, ServiceProviderOptions options)
at Microsoft.Extensions.DependencyInjection.DefaultServiceProviderFactory.CreateServiceProvider(IServiceCollection containerBuilder) 
at Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter`1.CreateServiceProvider(Object containerBuilder)
at Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider()
at Microsoft.Extensions.Hosting.HostBuilder.Build()
at ECC.Program.Main(String[] args) in C:SomePathProgram.cs:line XXX

如有任何帮助,我们将不胜感激!

当您使用FluxorComponent作为基础时,它将扫描组件中的所有IState<T>属性并订阅它们。每当此状态的值发生更改时,FluxorComponent就会调用StateHasChanged来重新呈现组件。

当组件被释放时,FluxorComponent将删除所有订阅以避免内存泄漏,并尝试呈现已释放的组件。

在你的情况下,这就是正在发生的事情

  1. Blazor禁用组件,使其无法再渲染
  2. 它调用Dispose
  3. 被覆盖的Dispose调度一个操作,该操作提供一个新状态
  4. IState<T>的订阅将触发,StateHasChanged将针对无法呈现的组件执行

修复方法是首先调用base.Dispose(disposing),以便FluxorComponent基类可以在您调度操作之前取消订阅该状态。

最新更新