I am using Blazor COMPONENTS mixed in with my .NET MVC app. I'm slowly porting over away from MVC and into full blazor. The problem I have is, because we have a persistent blazor server connection, it's a long-standing "request" which means it holds on to DB connections. IF a transaction is in flight, until the Blazor server connection is severed, the DB is locked to other pages! I'm currently getting around it by creating a new scope on every request and not injecting any services into my blazor component and instead resolving them at runtime in each method handler. Is that really the only solution? Or is there some way to set this up that it works properly with Blazor server?
I've searched around and haven't been able to find anything in the documentation that might point me in the right direction.
5 Answer(s)
-
0
- In ABP, the HTTP request is not the “scope” that keeps
DbContext/transactions alive; the Unit Of Work (UOW) is. ABP’s UOW defines the database connection/transaction boundary and disposes it when the UOW ends. - In a Blazor Server app, a user’s circuit is long-lived, but that should not mean your EF Core transaction stays open. It only happens if you create/keep a UOW open across multiple UI events (or you hold on to an EF
IQueryable/DbContext-backed object and use it later).
What to do (recommended ABP approach)
Do not inject repositories/
DbContextand run EF code directly inside Blazor components.
Keep components “UI only” and call Application Services (or domain services) instead.Ensure each operation is wrapped in its own UOW (short-lived).
- ABP automatically creates a UOW around application service methods (via interceptor), so this is usually already handled when you call an app service from the component.
- If you have a service method that is not an application service (or you disabled UOW conventions), explicitly use
[UnitOfWork]orIUnitOfWorkManager.Begin(...)to scope it to that single method call.
Example (explicit UOW):
public class MyBlazorActionService : ITransientDependency { private readonly IUnitOfWorkManager _unitOfWorkManager; private readonly IMyEntityRepository _repo; public MyBlazorActionService(IUnitOfWorkManager unitOfWorkManager, IMyEntityRepository repo) { _unitOfWorkManager = unitOfWorkManager; _repo = repo; } public async Task DoSomethingAsync() { await using var uow = _unitOfWorkManager.Begin(isTransactional: true); // db work here await uow.CompleteAsync(); } }Never keep a transaction open waiting for user interaction.
In Blazor it’s tempting to “start transaction on button A, commit on button B”. Don’t do that. Between those clicks you may block rows/tables (especially in PostgreSQL depending on what you touched). Instead:- design operations as single-command units (one UI event → one backend method → one UOW/transaction), or
- use an application-level workflow (save draft / saga-like approach), not a single DB transaction spanning UI time.
Avoid holding EF Core queryables across events.
Don’t cacheIQueryable, repositories, or anything that implicitly depends on an ambient UOW and then enumerate it later. Materialize results inside the same method/UOW (e.g.,ToListAsync) and return DTOs to the component.
About your workaround
Creating a new scope per handler can work, but it’s not the intended pattern in ABP. The intended pattern is: Blazor component → call app service → app service method runs in its own UOW → done. That gives you the “per action” scope you want without manual service resolution.
https://abp.io/docs/latest/framework/architecture/domain-driven-design/unit-of-work
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
- In ABP, the HTTP request is not the “scope” that keeps
-
0
I'm pretty sure I tried this, but I'll try again. I'm using AppServices heavily, but I found that if I had two pages open: one that created an object and the other that listed objects of that type, the page that listed the objects did NOT get the new item until AFTER I refreshed the first screen, meaning it was still holding on to a DB transaction. To get around it I create a new scope and then resolve my app service from there. I guess that's because I'm injecting my AppService into the blazor component and that app service is in ONE Unit Of Work -- not each method call to my App Service. That's where it's getting stuck.
I can try to decorate my blazor componetns with UnitOfWork and see if that helps create a new scope, so even if my parent AppService is long lived then each Blazor handler method will be a short-lived circuit on it. That feels fragile though.
-
0
hi
because we have a persistent blazor server connection, it's a long-standing "request" which means it holds on to DB connections. IF a transaction is in flight, until the Blazor server connection is severed, the DB is locked to other pages!
Can you share your code?
I will try to reproduce your problem in a new template project.
Thanks.
-
0
I can't share my code. It's huge and specific to my work. So let me flip the question. Can you provide a sample component and how it should be done? A blazor component, with some kind of click handler, that needs to use some services, do its work, and NOT lock/block. What gets injected? What gets inherited? What attributes, if any, do you use (e.g. in the clck handler in blazor do you add [UnitOfwork])? I'm just looking for best practices patterns. I wasn't able to find anything concrete about it.
-
0
hi
For tiered and no-tiered Blazor Server:
- tiered
Your app service call requests are made through HTTP requests to the API. In other words, the actual application service code runs in the API, not in Blazor Server.
- no-tiered.
The application service code runs within a monolithic application.
Your application service might create transactions or use strict data isolation levels to lock data, so I need you to provide a simple reproduction project.
Multiple application service calls won't block other application services or pages.Multiple application service calls won't block other application services or pages.
Thanks.