asp.net Blazor并发冲突(同一用户,后端代码)

dgsult0t  于 2023-04-22  发布在  .NET
关注(0)|答案(3)|浏览(275)

我们正在评估Blazor作为大型企业应用程序的技术堆栈,我有一些问题。
假设我们有两个文本框,用户可以编辑它们。当用户编辑第一个文本框时,会执行一段后端代码,这段代码可能会在第一次编辑后的一秒内编辑第二个文本框(绑定到属性)。这可能会导致问题,因为用户可能同时在第二个文本框中输入了内容。
我们可能会在后台代码运行时阻止用户输入,但是,我希望在服务器计算期间不阻止用户输入。有什么好的处理方法吗?完美的解决方案是,由于并发编辑而出现弹出窗口,用户可以决定做什么。至少他被告知他的值可能会被覆盖。
有什么想法吗?谢谢

sd2nnvve

sd2nnvve1#

Blazor可选输入值

不确定这是否有帮助,但在Blazor Server中,您可以对所描述的内容进行各种创造性组合。
这样做的一种方法如下:与其将第一个输入绑定到第二个输入,你只需调用一个方法并运行你的await-async服务器进程,它会根据第一个输入返回一个新的修改值。用户可以在 asynchronous 服务器Task处理时继续填写第二个输入(或任何输入),并返回新的修改值。
当对服务器的await调用返回新值时,一个 JSInteropt 调用会触发一个常规的 JavaScriptwindow.confirm(x)弹出窗口,并在浏览器中显示一个问题。他们可以选择保留自己的值,单击"ok",或者接受第二个输入单击"cancel"的值。这不是很吸引人,但它给了你一个想法。注意:在下面的这个例子中,我没有修改来自服务器调用的第一个输入,只是将原始值传递给第二个输入。
您可以将下面的代码粘贴到任何Blazor Server组件中并对其进行测试:

// Inject the JSInterop feature to use custom JavaScript in Blazor
@inject IJSRuntime JS

<input type="text" @onchange="MyMethod" /><br />
<input type="text" value="@MyValue" />

@code {
    private string? MyValue { get; set; } = "";
    private async Task MyMethod(ChangeEventArgs args)
    {
        // Add your await method call to the long-running
        // server process here.
        string newvalue = await GetNewServerValue(args);

        // Call a JavaScript window.confirm with a choice for the user
        string x = "Do you want to keep your own input?";
        bool result = await JS.InvokeAsync<bool>("confirm", x);

        // If the user does not want to keep what they typed,
        // they can click cancel in the confrim box and the first input
        // value replaces their own using the property below.
        if (!result) {
            //MyValue = newvalue;// use the servers modified value
            MyValue = args?.Value?.ToString() ?? string.Empty;
        }
    }

    private async Task<string> GetNewServerValue(ChangeEventArgs args)
    {
        // I added a 5 second wait to simulate time for a user to enter
        // some text in the second input while waiting for the
        // server's response.
        Thread.Sleep(5000);
        // Run your process and return your modified value...
        return "new value";
    }
}

我知道这有点草率。我不喜欢像这样使用JSInterop或JavaScript prompts。当你做这些技巧或超过Blazor Server SignalRMaximumReceiveMessageSize限制32 k时,你会冒着在SignalR外部调用JavaScript * 全局窗口对象 * 并丢弃 WebSocket 连接的危险。但是Blazor给了你创造性的能力来添加各种各样的好东西。

使用自定义弹出窗口的更好方法是构建一个新的Blazor Component in HTML with CSS作为<div>,并使用z-index in CSS在屏幕上显示该层,然后根据上面的事件显示和隐藏它。或者这可以是一个微小的blazor组件,它可以滑动到输入旁边的视图中,用户可以快速单击选择。

但至少这向您展示了Blazor中处理这类场景的多种方法之一。

rggaifut

rggaifut2#

正如其中一个回复中所指出的,并发问题是相同的,无论框架如何。* 然而 * 实现可能会有很大的不同,这取决于你是使用Blazor Server还是Blazor WebAssembly。
1.Blazor服务器:由于在客户端没有执行任何代码,因此您可以使用Singleton DI来解决并发问题(无论何时需要)。因此,Singleton服务会根据用户ID(例如“x”)跟踪正在修改的业务对象。所有其他用户都会被告知用户“x”正在修改项目-类似于您在Google文档/工作表中看到的内容-非常容易接近真实的。
1.Blazor WebAssembly:所有代码都完全在浏览器中运行。除非通过SignalR hub或其他方式明确通知,否则两个用户无法知道他们正在修改的实体。
最后,你总是可以乐观地解决并发问题(比如EF Core)。但请记住,使用WA,并发冲突可能只在提交时才知道-除非显式地解决。

tzcvj98z

tzcvj98z3#

完美的解决方案是,由于并发编辑而出现一个弹出窗口,用户可以决定要做什么。至少他被告知他的值可能会被覆盖。
有什么想法吗
所以,按照你所描述的去做,问题在哪里?

<input @bind="Model.PropertyA" @bind:after="OnPropertyAChanged" />
<input @bind="Model.PropertyB" />

@code 
{  
    async Task OnPropertyAChanged()
    {
         var propertyBBackup = Model.PropertyB;
         var propertyBNew = await _someService.CalculatePropertyBAsync(Model.PropertyA);
         if (propertyBNew != Model.PropertyB) //value changed by CalculateNewPropertyB
         {
              if (propertyBBackup != Model.PropertyB) //value changed by user
              {
                  var toast = await ToastService.Show(....);
                  toast.ClickedYes += 
                  {
                      Model.PropertyB = propertyBNew;                       
                  }
              }
         }
    }

}

如果你熟悉Reactive Extensions,我会构建一个操作符(或扩展方法)来检测特定字段的更改,同时等待源observable(CalculatePropertyBAsync)发出一个值并使其可重用。类似于:

_someService.CalculatePropertyBAsync(Model.PropertyA).ToObservable()
    .DetectConflict(() => Model.PropertyB, onConflict: LetUserDecideInDialog)
    .Subscribe(result => Model.PropertyB = result);

其优点是,您可以将其与Switch/SelectMany(在RxJS中称为SwitchMap)或Throttle操作符组合使用,以在PropertyA更改太频繁时处理并发问题等。

相关问题