Blazor,这是单例滥用吗? (共享 iframe 值)

Blazor, Is this Singleton abuse? (Sharing iframe values)

首先,让我解释一下为什么我认为我需要一个单例。我正在将多个 Hosted Checkout 支付处理器集成到我的 Blazor Server 应用程序中。它们的工作原理如下;

这就是问题所在PaymentComplete.razor 托管在 iframe 中,因此在单独的范围内处理。 HostedCheckoutServicePaymentComplete.razor 中引发的任何 属性 更改或事件都不会是 Index.razor。这使得(几乎?)无法将 iframe 中的信息合并到 Index.razor.

的范围内

显然可以解决此问题的方法是将 HostedCheckoutService 注册为 单例 。现在的问题是,当一个客户端从 PaymentComplete.razor 引发事件时, 所有 个客户端将不得不处理它。

为了解决这个问题,我创建了一个 IndexBase,其中包含一个名为 EventTargetId 的唯一 属性。当iframe支付完成后,returnurl到PaymentComplete.razor查询字符串中会包含EventTargetId

IndexBase.cs

<iframe style="width:100%;height:50vh" src="@HostedCheckoutFrameSrc " frameborder="0" ></iframe> 


public class IndexBase : ComponentBase, IDisposable
{
    [Inject] NavigationManager NavigationManager { get; set; }
    [Inject] HostedCheckoutService HostedCheckoutService { get; set; }
    [Inject] PaymentApi PaymentApi { get; set; }

    public string HostedCheckoutFrameSrc { get; set; }
    public string EventTargetId { get; set; } = Guid.NewGuid().ToString();

    protected override void OnInitialized()
    {
        HostedCheckoutService.OnPaymentComplete += PaymentComplete;
    }

    public void Dispose()
    {
        HostedCheckoutService.OnPaymentComplete -= PaymentComplete;
    }

    private void PaymentComplete(string eventTargetId, string paymentJson)
    {
        // Hosted checkout iframe has returned a successfull payment.
        // Do something, send order, notification, ect.
    }

    public async Task InitializePayment()
    {
        string returnUrl = NavigationManager.BaseUri + $"/PaymentComplete?eventTargetId={EventTargetId}";
        InitializePaymentResponse response = await PaymentApi.CreatePaymentRequest(returnUrl);
        // Set iframe src property to third party payment providers url.
        // When customer completes third party payment url, the iframe redirects to PaymentComplete.razor (returnUrl).
        HostedCheckoutFrameSrc = PaymentApi.baseUrl + response.PaymentId;
    }
}

PaymentComplete.razor(从第三方 url 重定向,在 iframe 内托管)

此页面将从查询字符串中获取 EventTargetId 并在我们的单例服务上引发事件。

[Inject] NavigationManager NavigationManager { get; set; }
[Inject] PostFormService PostFormService { get; set; }
[Inject] HostedCheckoutService HostedCheckoutService { get; set; }

protected override async Task OnInitializedAsync()
{
    // We face double render problem, but Form values will be null on secord render anyways.
    if (PostFormService.Form != null)
    {
        NavigationManager.TryGetQueryString<string>("eventTargetId", out string eventTargetId);
        string paymentJson = PostFormService.Form?["PaymentResponse"];
        HostedCheckoutService.PaymentCompleted(eventTargetId, paymentJson);
    }
}

在我的单例 HostedCheckoutService 中,我使用 EventTargetId.

过滤掉所有订阅者
public class HostedCheckoutService 
{
    public event Action<string, string> OnPaymentComplete;

    public void PaymentCompleted(string eventTargetId, string paymentJson)
    {
        // Instead of raising the event for every instance attached to this action
        // only raise the event for the specified target.
        var instance = OnPaymentComplete.GetInvocationList()
                            .Where(d => d.Target is IndexBase && ((IndexBase)d.Target).EventTargetId == eventTargetId)
                            .FirstOrDefault();
        instance?.DynamicInvoke(eventTargetId, paymentJson);
    }
}

终于有问题了!这看起来是不可接受的单例事件使用还是有人有更好的方法? 即使每个客户端都不会处理该事件,对 GetInvocationList() 的调用仍将包含每个订阅的列表 class.

注意: 每个事件订阅者实际上并不是完整的 IndexBase class。这将是一个简单的支付组件(我简化了这个例子)。

我主要关心的是在事件上调用所有已注册方法的缩放。

因为我不知道 HostedCheckoutService 还能做什么,如果有一个单例 PaymentTransactionService 包含一个简单的 Guids against Actions 集合 - 可能还有操作超时系统的注册时间。 Index 在 PaymentTransactionService 上调用 Register 方法来注册它的 Guid 和 Action。 - 显然是 ReRegister 方法 DisposesPaymentCompletePaymentTransactionService 上调用了 TransactionComplete 方法。它会检查它的列表并执行已注册的操作(如果有)——如果没有则记录错误。您可以使用每个 PaymentComplete 调用来启动检查超时和删除过期注册的管理例程。