【问题标题】:Do API controllers use a different instance of a service than Blazor components?API 控制器是否使用与 Blazor 组件不同的服务实例?
【发布时间】:2021-07-26 10:08:36
【问题描述】:

我使用此Best way to share data between two child components in Blazor 更新服务类中的 blazorcomponent 以添加通知。

我将此添加到 API 控制器的 POST 中,并将通知服务注入到构造函数中。

然后像这样显示到 Blazor 组件中: 但由于某种原因,这只适用于 services.AddSingleton,而不适用于 services.AddScoped。

但是,当您像这样在 2 个 blazor 组件之间使用通知服务时,services.AddScoped 确实有效。 (这是一个不同的组件)

那么,API 控制器是否使用不同的通知服务实例?还是我做错了什么?

提前谢谢你。如果您需要更多信息,请告诉我。

【问题讨论】:

  • 你为什么要问? Blazor Server 和 Web API 做的事情非常不同。在 Blazor Server 应用中,共享数据特定于当前用户的会话。在 Web API 中,没有“当前用户”,因此您需要使用会话存储或其他更好的机制来缓存请求之间的数据
  • 我在 blazor 项目中创建了 API 控制器。所以我认为他们都会使用相同的服务实例。感谢您提供解决方案
  • 你首先想做什么?您是否试图在两者之间共享状态?两者都在服务器上运行,因此将您想要的任何数据传递给 API 控制器都很便宜。如果要保留某些用户状态,可以使用 Blazor 服务。但是,如果您想从外部服务修改用户数据,则必须自己管理此存储,并且可能使用数据库或其他持久存储
  • 我有一个 Azure 队列无服务器触发器。在该函数中,我解析一个文件,我想将状态发送到我的 blazor 项目。一旦它进入 API 控制器,我想更新 UI。
  • 这很棘手,因为当您接到外部电话时没有用户。 Web API 不知道应该更新哪个 UI 同一用户可能有多个活动选项卡,或者没有。即使您使用单例或数据库存储,此问题仍然存在。您可以使用 SignalR 找到正确的用户并发送带有更改/通知数据的通知。

标签: c# api dependency-injection blazor blazor-server-side


【解决方案1】:

API 控制器是否使用与 Blazor 组件不同的服务实例?

对于作用域服务,答案肯定是,对于单例服务,答案肯定是yes,但它会变得复杂。但我怀疑这不是您真正的问题,真正的 问题是如何为两者存储用户特定的数据。或者从外部服务修改特定用户的数据

为什么服务不同

创建一个新的控制器实例来处理每个 HTTP 请求。就 DI 而言,HTTP 请求定义了一个范围,因此为每个 HTTP 请求创建一个新的服务实例,并在处理完请求后释放。

这是一件好事,因为这意味着即使发生错误,也会释放昂贵的范围服务(如 DbContext)。对于 DbContext,这提供了开箱即用的 transaction-per-request 语义,无需任何额外代码。

使用 Blazor Server,事情更多复杂。在这种情况下,Blazor Server 为每个“用户电路”定义一个范围,该范围本质上是一个选项卡。这意味着只要标签处于活动状态,范围内的对象就会保持活动状态当导航发生在客户端上时。 MVC 部分的行为方式与 ASP.NET Core MVC/Web API 相同

这类似于桌面应用程序的行为方式,当人们认为像 DbContest 这样的范围服务将被“自动”处置时,可能会导致令人讨厌的意外。当您致电 SaveChangesAsync 时,您可能最终会保留您认为已被丢弃的更改。

因此,即使您尝试在 Blazor Server 中使用范围服务,您最终也可能会在 Blazor 组件中使用长期服务,而在 Web API 控制器中使用短期服务。

实际上,由于使用了不同的作用域,所以两个服务甚至找不到对方。

DI 和 Scope 在 Blazor Server 中的工作原理

这在ASP.NET Core Blazor dependency injection 中有记录。在Service Lifetime 中解释了作用域生命周期的差异:

Blazor 服务器托管模型支持跨 HTTP 请求的 Scoped 生命周期,但不支持客户端上加载的组件之间的 SignalR 连接/电路消息。

应用程序的 Razor 页面或 MVC 部分正常处理范围服务,并在页面或视图之间或从页面或视图导航到组件时在每个 HTTP 请求上重新创建服务。

在客户端上的组件之间导航时不会重建作用域服务,其中与服务器的通信是通过用户电路的 SignalR 连接进行的,而不是通过 HTTP 请求。

在客户端的以下组件场景中,由于为用户创建了新电路,因此重构了作用域服务:

  • 用户关闭浏览器窗口。用户打开一个新窗口并导航回应用程序。
  • 用户在浏览器窗口中关闭应用程序的最后一个选项卡。用户打开一个新选项卡并导航回应用程序。
  • 用户选择浏览器的重新加载/刷新按钮。

在外部调用后通知用户的标签

来自 cmets:

我有一个 Azure 队列无服务器触发器。在该函数中,我解析一个文件,我想将状态发送到我的 blazor 项目。一旦它进入 API 控制器,我想更新 UI。

这很棘手。在这种情况下,Web API 本质上是在充当另一个用户。 Blazor 服务器选项卡需要更新以响应本质上是外部事件的事件。

由于它是 Blazor 服务器,假设没有使用负载平衡,所有用户电路都在处理 API 请求的同一进程中运行。可以在 API 控制器中引发“事件”并让 Blazor 组件监听它。

幸运的是,这正是像 Blazor.EventAggregator 这样的库所实现的。

事件聚合器服务是一个单例,注册于:

public void ConfigureServices(IServiceCollection services)
{
    services.AddEventAggregator();
}

假设消息只接受一个文件名和一个状态:

public record FileEvent(string file,string status);

Web API 控制器将充当发布者:


class MyController:ControllerBase
{

    private IEventAggregator _eventAggregator { get; set; }

    public MyController(private IEventAggregator eventAggregator)
    {
        _eventAggregator=eventAggregator;
    }

    ...

    [HttpPost]
    [AllowAnonymous]
    public async Task<ActionResult> Post(BlobInfo blobInfo)
    {
        await _eventAggregator.PublishAsync(new FileEvent(blogInfo.BlobName,"OK");
        return Ok();
    }
}

关心的 Blazor Server 组件可以注入IEventAggregator,监听事件,指定他们关心的事件并处理它们。

在每个组件的代码隐藏中,服务可以注入:

[Inject]
private IEventAggregator _eventAggregator { get; set; }

该类还需要为它关心的事件实现IHandler&lt;T&gt;接口:

public class MyComponent : ComponentBase, IHandle<FileEvent>
{
    ...
    List<string> _messages=new List<string>;

    public async Task HandleAsync(FileEvent message)
    {
        _messages.Add($"{message.Name} worked");
        await InvokeAsync(StateHasChanged());
    }

...
}

Razor 代码实际上不需要更改:

@foreach(var value in _messages)
{
   <p>@value</p>
}

【讨论】:

  • “范围服务的答案肯定是肯定的。”您的意思是注入 Blazor 组件的对象与注入 Web Api 的对象相同吗?请你证明你的主张。
  • 我可以很容易地通过代码示例证明你错了,但你首先要通过代码示例证明你是对的。
  • @enet 阅读文档。我说的和你理解的完全相反。我说不是,也不能一样。我写了一个标题为Why the services are different 的整个部分,你是怎么读到the object injected into the Blazor component is the same one injected into a Web Api 的?
  • 您稍后添加了引号。但如果你说:“我说不是”,那你为什么说:这是不正确的。对于初学者来说,使用 Blazor Server,一切都在同一进程中运行并使用相同的 DI 基础架构。 DI 不关心项目,它关心范围。 " 我在这里看到了想法的改变,除了提到范围,这在 Web Api 的情况下确实不相关,因为范围通常限定为请求的持续时间。
  • 我仍然支持我的回答:“API 控制器不使用注入到 Blazor 组件中的相同服务实例。”请不要告诉我读这里读那里,只是用代码证明你的主张......新的主张是,如果一个服务被定义为 Singleton,那么它由 Blazor Server App 和一个 Web Api App 共享。跨度>
猜你喜欢
  • 1970-01-01
  • 2019-06-30
  • 1970-01-01
  • 2022-01-24
  • 2017-06-10
  • 2022-11-07
  • 2013-03-02
  • 2017-08-08
  • 1970-01-01
相关资源
最近更新 更多