Blazor 服务器应用程序和 IDbContextFactory 未处理
Posted
技术标签:
【中文标题】Blazor 服务器应用程序和 IDbContextFactory 未处理【英文标题】:Blazor Server App and IDbContextFactory not disposing 【发布时间】:2022-01-17 01:26:37 【问题描述】:我有一个需要间接连接到 EF 核心数据库上下文的 blazor 服务器应用程序。
所有 blazor 组件都不会直接注入 dbcontext 的实例。我正在使用调解员来处理所有业务操作。
目前我看到的文档建议使用 IDbContextFactory。我试了一下,但我没有看到工厂创建的 DbContext 被处置。注入 IDbContext 的服务不会在页面更改时或任何其他时间释放。
public class QueryHandler : IQueryHandler<Query, Entity>, IDisposable
private readonly DbContext dbContext;
public QueryHandler(IDbContextFactory factory)
dbContext = factory.CreateDbContext();
public Task Handle(Query query)
/// do whatever needs to be done.
public void Dispose()
dbContext.Dispose(); // <-- Dispose never gets called.
我错过了什么吗?
【问题讨论】:
这与 EF Core 和IDbContextFactory
无关。据我了解,问题是您的QueryHandler
未处理。
我正在使用 Microsoft.Extensions.DependencyInjection。据我了解,它会根据服务的注册范围自动处置服务。
范围是多少?在 Blazor Server 中,它是客户端电路 - 基本上是整个会话
有一个新的基础组件有助于控制服务提供者范围的生命周期。 OwningComponentBase
& OwningComponentBase<TService>
source.dot.net/#Microsoft.AspNetCore.Components/…
工厂不是一次性的,它创建的对象是一次性的。但是它创建的对象没有在 DI 容器中注册,您有责任处理它们。工厂的标准行为(与使用带有一次性对象的 new
运算符相同)。
【参考方案1】:
使用 DbContextFactory 的目的是让每个方法都有一个 DbContext。 正是因为 Blazor 没有提供有用的 Scopes 来处理这个问题。
public class QueryHandler : IQueryHandler<Query, Entity> //, IDisposable
...
public QueryHandler(IDbContextFactory factory)
_factory = factory;
public Task Handle(Query query)
using var dbContext = _factory.CreateDbContext();
/// do whatever needs to be done.
//public void Dispose()
这样 DI 容器和工厂只管理 DbContext 的配置。 DbContext 的生命周期管理是手动的。 Factory 是一个简单的 Transient 对象,不拥有任何资源。
手动管理通常使用 using 语句或 using 声明,但 Blazor 还提供 OwningComponentBase。我没有看到它被大量使用。
【讨论】:
谢谢,这很有帮助。 仅仅因为它们的工作方式与 MVC 不同,并不意味着它们没有用。在 Blazor WASM 中,范围是页面/组件。在 Blazor Server 中,它是电路。 Blazor 是一个 SPA,因此“范围”比单个请求要广泛得多 @enet 是的,using
指令应该在这里工作
@lenniep,你确定吗?你能提供一个这样使用它的代码的链接吗?也许来自文档...【参考方案2】:
在服务器中,DI 容器存在于 Hub 会话的生命周期中,而在 WASM 中存在于应用程序的生命周期中。在容器内创建的任何服务对象,无论是 Scoped 还是 Transient,实现 IDisposable
,在 DI 容器本身被销毁之前都不会被释放。你没有明确QueryHandler
的范围,但如果它是暂时的,那是个坏消息。您将继续创建新的 DBContext,而不会释放旧的。
DbContextFactory 的目的是创建使用的工作单元 DbContext 实例,然后快速正确地处理这些实例。您需要采用这种方法,因为 DB 访问几乎肯定是异步的。使用单个上下文,您将很快遇到您正在等待一个查询完成同时尝试在另一个操作中使用相同上下文的情况。
Henk 的回答向您展示了如何使用和使用工厂创建的上下文。
【讨论】:
"In Server the DI container exists for the lifetime of the Hub Session and in WASM the lifetime of the Application. "
:是的。 Any service objects created within the container, whether Scoped or Transient, are not Disposed until the DI container itself is destroyed.
:错误。如果 DI 容器创建的作用域和瞬态对象实现了 IDisposable 接口,它们将被释放。看到这个:github.com/aspnet/DependencyInjection/blob/…
@HenkHolterman(和我)是正确的,是的。重新检查您突出显示的代码。 _transientDisposables
是实现IDisposable
的瞬态对象列表。 DI 容器保留了对它们的引用,并且仅在 DI 容器本身 Disposed 时才释放它们。这里有一篇关于这个主题的文章 - docs.microsoft.com/en-us/dotnet/core/extensions/…。
我已将implementing
IDisposable`` 添加到我的答案中,因为某些阅读可能无法理解Disposed
的隐含含义!
好吧,至少这篇文章仍然是最新的。但我错了,容器确实 Dispose Transient 对象。幸运的是,帖子还声明你不应该使用它,它很愚蠢并且会泄漏内存。
Any service that the built-in container creates in order to fill a dependency, which also implements IDisposable, will be disposed by the container at the appropriate point. So Transient and Scoped instances will be disposed at the end of the request (or more accurately, at the end of a scope), and Singleton services will be disposed when the application is torn down and the ServiceProvider itself is disposed.
: andrewlock.net/…以上是关于Blazor 服务器应用程序和 IDbContextFactory 未处理的主要内容,如果未能解决你的问题,请参考以下文章
.NET 6 Web API 和 Blazor 服务器应用程序之间的回调侦听器
Blazor University 介绍 - 什么是 Blazor?