如何同时从不同的 Blazor 组件进行数据调用?

Posted

技术标签:

【中文标题】如何同时从不同的 Blazor 组件进行数据调用?【英文标题】:How do I make data calls from different Blazor components simultaneously? 【发布时间】:2021-06-02 10:48:20 【问题描述】:

我是 Blazor 的新手,我正在尝试制作一个包含多个单独组件的页面来处理庞大的表单。每个单独的组件都覆盖了表单的一部分。

我面临的问题是我的每个组件都需要从后端访问数据,而且并非每个组件都使用相同的数据。当页面加载时,每个组件都会尝试从服务器获取数据,这会导致 Entity Framework 出现问题。

在前一个操作之前在此上下文上启动了第二个操作 完全的。这通常是由不同的线程使用相同的 DbContext 的实例。

这显然是由于我的组件是同时初始化的,并且都尝试同时加载数据。我的印象是 Blazor 中设置 DI 的方式,这不是问题,但确实是。

这是我的模板中的组件:

<CascadingValue Value="this">
    <!-- BASE DATA -->
    <CharacterBaseDataView />

    <!-- SPECIAL RULES -->
    <CharacterSpecialRulesView />
</CascadingValue>

这是我的组件的初始化方式:

protected async override Task OnInitializedAsync()

    CharacterDetailsContext = new EditContext(PlayerCharacter);
    await LoadCharacterAsync();


private async Task LoadCharacterAsync()

    PlayerCharacter = await PlayerCharacterService.GetPlayerCharacterAsync(ViewBase.CharacterId.Value);
    CharacterDetailsContext = new EditContext(PlayerCharacter);

当具有上述代码的两个组件在同一个视图中时,会发生上述错误。我使用同步版本“OnInitialized()”线程并简单地丢弃任务,但这并没有解决错误。

是否有其他方法可以调用数据以避免出现此问题?还是我走错了路?

【问题讨论】:

【参考方案1】:

您在 EF 中使用异步操作时遇到了一个常见问题 - 两个或多个操作尝试同时使用相同的上下文。

看看MS Docs article about EF DBContexts - 下面有一个部分专门针对 Blazor。它解释了使用 DbContextFactoryCreateDbContextunits-of-work 创建上下文,即每个操作一个上下文,因此两个异步操作每个都有一个单独的上下文。

【讨论】:

太好了,解决了。作为未来读者的注意事项:上面提到的文档依赖于 Core 5.0 中提供的函数/类。我仍在使用 3.0,必须升级才能使其正常工作。如果您使用 IdentityFramework,您还需要在 StartUp 类中进行常规上下文实例化。【参考方案2】:

最初为了解决线程问题,我使用 DbContextFactory 为每个操作创建上下文 - 但是这导致跨组件的数据库不一致问题,我意识到我需要跨组件跟踪更改。

因此,我将 DbContext 保持在范围内,并且不会在每次操作之前创建新上下文。

然后我调整了我的OnInitializedAsync() 方法来检查对数据库的调用是否已完成,然后再通过我注入的服务进行这些调用。这对我的应用非常有效:

 @code 
        static Semaphore semaphore;
        //code ommitted for brevity
        
         protected override async Task OnInitializedAsync()
         
             try
             
                //First open global semaphore
                semaphore = Semaphore.OpenExisting("GlobalSemaphore");

                while (!semaphore.WaitOne(TimeSpan.FromTicks(1)))
                
                    await Task.Delay(TimeSpan.FromSeconds(1));
                
                //If while loop is exited or skipped, previous service calls are completed.
                ApplicationUsers = await ApplicationUserService.Get();        
             
             
             finally
             
                try
                
                    semaphore.Release();
                
                catch (Exception ex)
                
                    Console.WriteLine("ex.Message");
                
            
        

【讨论】:

我的应用程序中的数据流充分分离,几乎不可能在数据中出现不一致,但我同意你的建议可能是最好的方法,如果你想覆盖你的所有基础.我会亲自将您的示例代码移动到一个返回承诺的服务中,这样您就可以按照await ConcurrentCallHelperService.AwaitContextQueueAsync() 的方式做一些事情,因此您需要维护的重复代码更少,但除此之外,这是一个明显的改进。

以上是关于如何同时从不同的 Blazor 组件进行数据调用?的主要内容,如果未能解决你的问题,请参考以下文章

如何从服务器端 Blazor 应用程序中的 Blazor 组件调用 razor 页面而不导致页面刷新

Blazor:如何使用来自具有 2 个不同状态的 2 个不同页面的组件

从 Blazor 中的页面组件调用 MainLayout 中的方法

如何从 blazor 中的子组件获取属性

如何以不同方式设置 Blazor 组件的样式

Blazor 组件之间使用 EventCallback 进行通信