API 控制器是不是使用与 Blazor 组件不同的服务实例?
Posted
技术标签:
【中文标题】API 控制器是不是使用与 Blazor 组件不同的服务实例?【英文标题】: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 找到正确的用户并发送带有更改/通知数据的通知。 【参考方案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<T>
接口:
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 共享。跨度>
以上是关于API 控制器是不是使用与 Blazor 组件不同的服务实例?的主要内容,如果未能解决你的问题,请参考以下文章
Blazor Web Api 返回 text/html 而不是 application/json
Blazor:如何使用来自具有 2 个不同状态的 2 个不同页面的组件