英文:
Blazor server - how long is a transient service lifetime
问题
From reading ASP.NET Core Razor component lifecycle I think that means a transient service, which has a lifetime of per request, then can be used for instances that are not thread safe (assuming I don't create background threads in my processing).
Yes there's several steps and it's possible to write code in OnInitialized() say that takes a couple of seconds. But fundamentally all the code is going to execute and render the page. And then done, the request is complete, and that transient instance is gone - forever.
So in terms of a DbContext, again assuming I'm not creating any background threads, while it's not safe to inject it as a singleton or scoped service, transient should be safe. Or am I missing something?
And if that is true, is there a way to tell AddDbContextFactory()
to provide a DbContext as a transient service? Looking at the relevant classes I did not see any way of setting the generated DbContext lifetimes. And declaring it as [Inject]
in a razor file connotates scope or singleton lifetime as it's there for the life of the component, not per request handled by the component.
Update:
I think I wrote the above wrong (or inartfully). My concern is the use of:
[Inject] private UserDbContext UserDbContext { get; set; } = default!;
This is created, as I understand it, by AddDbContextFactory. And I believe this is giving me a DbContext with a scope lifetime. And I'm concerned that this scope lifetime is problematic.
But I see that [Inject]
in pretty much every demo.
英文:
From reading ASP.NET Core Razor component lifecycle I think that means a transient service, which has a lifetime of per request, then can be used for instances that are not thread safe (assuming I don't create background threads in my processing).
Yes there's several steps and it's possible to write code in OnInitialized() say that takes a couple of seconds. But fundamentally all the code is going to execute and render the page. And then done, the request is complete, and that transient instance is gone - forever.
So in terms of a DbContext, again assuming I'm not creating any background threads, while it's not safe to inject it as a singleton or scoped service, transient should be safe. Or am I missing something?
And if that is true, is there a way to tell AddDbContextFactory()
to provide a DbContext as a transient service? Looking at the relevant classes I did not see any way of setting the generated DbContext lifetimes. And declaring it as [Inject]
in a razor file connotates scope or singleton lifetime as it's there for the life of the component, not per request handled by the component.
Update:
I think I wrote the above wrong (or inartfully). My concern is the use of:
[Inject] private UserDbContext UserDbContext { get; set; } = default!;
This is created, as I understand it, by AddDbContextFactory. And I believe this is giving me a DbContext with a scope lifetime. And I'm concerned that this scope lifetime is problematic.
But I see that [Inject]
in pretty much every demo.
答案1
得分: 4
关于更新:
> 我担心这个作用域的生命周期会有问题。
这确实是个问题。这也是添加并使用工厂的整个原因。所以你不应该注入一个 DbContext。AddDbContextFactory() 注册工厂和(Scoped)上下文这一事实会产生误导。你仍然没有范围来在注入时使用上下文。
它的实用性有限,可能会有一种情况(比如在启动时进行数据种植),你首先会手动创建一个范围,然后再使用 DbContext 进行注入。
> 但我在几乎每个演示中都看到了 [Inject]。
你能提供几个链接吗?在一个页面的生命周期内保留一个 DbContext 可以是可以接受的,但然后你需要在该页面上调用 Dispose()。这也是为什么发明了 OwningComponentBase。
将其交给垃圾回收也可以,上下文在管理连接方面很聪明。但这仍然非常不规范。
> 有没有办法告诉 AddDbContextFactory() 将 DbContext 提供为瞬态服务?
不,DbContext 是可释放的,所以你不应该这样做。
DbContextFactory 本身是瞬态的,你可以使用它来创建一个在每个带有 using
子句的方法中都具有范围的 DbContext:using var db = Factory.Create();
。因此,DbContext 是受资源管理和线程安全的。
> 这意味着瞬态服务,其生命周期为每个请求,然后可以用于不是线程安全的实例。
瞬态对象不与请求绑定,它们会在你的代码保留对它们的引用的整个生命周期内存在。
它可以用于不是线程安全的状态,但其主要用途是用于不需要被处置的无状态服务。
英文:
Re the Update
> And I'm concerned that this scope lifetime is problematic.
It certainly is. That is the whole reason for adding and using the Factory. So you should not Inject a DbContext. The fact that AddDbContextFactory() registers both the factory and the (Scoped) context is misleading. You still have no Scopes to use the context with injection.
It has limited usefulness, there might be a case (for instance DataSeeding on startup) where you first create a Scope (manually) and then use the DbContext with injection.
> But I see that [Inject] in pretty much every demo.
Could you link to a few? It can be acceptable to keep a DbContext for the duration of a Page, but then you need a Dispose() on that Page. This is where OwningComponentBase was invented for.
Leaving it to the GC works too, the context is smart about managing its Connection. But still, that is very sloppy.
> is there a way to tell AddDbContextFactory() to provide a DbContext as a transient service?
No, a DbContext is IDisposable so you shouldn't want that.
The DbContextFactory itself is Transient, you use it to create a DbContext that is scoped to each method with a using
clause: using var db = Factory.Create();
. So the DbContext is resource-managed and thread-safe.
> that means a transient service, which has a lifetime of per request, then can be used for instances that are not thread safe
Transient objects are not tied to the request, they live as long as your code holds a reference to them.
It can be used to for state that is not thread-safe but the primary use is for stateless services that don't need to be disposed.
答案2
得分: 2
In addition to @HH's answer.
This [UserDbContext] is created, as I understand it, by AddDbContextFactory
不是的,如果您像您所做的那样注入UserDbContext
,您将从DI容器中获取UserDbContext
定义的服务:这取决于您在DI设置中进行的配置。
要从工厂获取一个工作单元上下文,您需要通过调用_factory.CreateDbContext()
从工厂获取它 - 请参阅https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#using-a-dbcontext-factory-eg-for-blazor
在您的示例中:
[Inject] private IDbContextFactory<TDbContext> _factory { get; set; } = default!;
然后在您的方法之一中像这样使用它:
public Task ExecuteAsync()
{
using var dbContext = _factory.CreateDbContext();
// 在上下文上执行一些异步工作
}
您不需要担心上下文的管理:它们不是由DI管理的。您从工厂获取一个上下文,使用它,然后通过处理它来释放它。Dispose
实际上不会销毁它,它只是将它释放回工厂以供重用。
您是否考虑将数据管理和持久性抽象出组件以创建可测试的数据流程?
英文:
In addition to @HH's answer.
> This [UserDbContext] is created, as I understand it, by AddDbContextFactory
No, if you inject UserDbContext
like you have done, you are getting the UserDbContext
defined service from the DI container: whatever you've setup in your DI setup.
To get a unit of work context from the factory you need to get it from the factory by calling _factory.CreateDbContext()
- see https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#using-a-dbcontext-factory-eg-for-blazor
In you example:
[Inject] private IDbContextFactory<TDbContext> _factory { get; set; } = default!;
And then use it like this in one of your methods:
public Task ExecuteAsync()
{
using var dbContext = _factory.CreateDbContext();
// Do some async work on the context
}
You don't need to worry about the management of the contexts: they are not managed by DI. You get one from the factory, use it and then release it by disposing it. Dispose
doesn't actually kill it, it just releases it back to the factory for reuse.
Have you considered abstracting your data management and persistence out of your components into a testable data pipeline?
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论