Getting EF core exception when we have two component A and Component B where
Component B is added onComponent A both component calls respective service Service A and Service B on OnInitializedAsync().
like this I have register DbContext
services.AddDbContext<EFCorePreRenderIssueDbContext>(options =>
options.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=efcoreissue;Trusted_Connection=True;MultipleActiveResultSets=true"),
optionsLifetime:ServiceLifetime.Transient);
Sample Project : EFCorePreRenderIssue.zip
Here we should see the issue.
InvalidOperationException: A second operation started on this context before
a previous operation completed. This is usually caused by different threads using
the same instance of DbContext. For more information on how to avoid threading issues with
DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()
.NET Core SDK (reflecting any global.json):
Version: 3.1.100-preview1-014459
Commit: ac3b59712d
Runtime Environment:
OS Name: Windows
OS Version: 10.0.18362
OS Platform: Windows
RID: win10-x64
Base Path: C:\Program Files\dotnet\sdk\3.1.100-preview1-014459\
Host (useful for support):
Version: 3.1.0-preview1.19506.1
Commit: bbf5542781
.NET Core SDKs installed:
2.2.100 [C:\Program Files\dotnet\sdk]
3.0.100-preview8-013656 [C:\Program Files\dotnet\sdk]
3.1.100-preview1-014459 [C:\Program Files\dotnet\sdk]
.NET Core runtimes installed:
Microsoft.AspNetCore.All 2.2.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All]
Microsoft.AspNetCore.App 2.2.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.0.0-preview7.19365.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 3.1.0-preview1.19508.20 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 2.2.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.0.0-preview7-27912-14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 3.1.0-preview1.19506.1 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.0.0-preview7-27912-14 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 3.1.0-preview1.19506.1 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
To install additional .NET Core runtimes or SDKs:
https://aka.ms/dotnet-download
It looks that prerendering is multithreaded. You have an async call to entity framework in Service A and before you get the result Blazor framework initializes your Component B and here you have another async call to entity framework from Service B.
AddDbContext call registers DbContext as scoped service. You can register it as transient and your problem will disappear.
c#
services.AddDbContext<EFCorePreRenderIssueDbContext>(options =>
options.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=efcoreissue;Trusted_Connection=True;MultipleActiveResultSets=true"),
contextLifetime:ServiceLifetime.Transient,
optionsLifetime:ServiceLifetime.Transient);
In your code there is only optionsLifetime:ServiceLifetime.Transient. You have to add contextLifetime:ServiceLifetime.Transient.
There is a way to solve the problem described, but I think it is a bad solution and you should block multi-threaded component code execution during prerendering.
Project attached to this issue is simple but represents a typical programming pattern for Blazor Wasm application with prerendering. You have services which read data from database. Each service has DbContext injected in constructor. These services are used by components directly during prerendering. The same service implementation is used in controllers for Web API. In Web API it is usually better to have scoped DbContext and it is the default if you call AddDbContext without additional parameters. The same usually applies to services which implement our business logic. It is faster and more GC friendly because you have only one DbContext per single Web API call. It is often more convenient because you can do a few database operation in a single transaction easily.
In Blazor we should compose a single web page from smaller components. Each component can eventually read some information from database in OnInitializedAsync(). Currently when components are prerendered Blazor calls these functions concurrently. These creates problem in scoped DbContext. As I written above we can register DbContext as transient to solve the problem during prerendering, but unfortunately we will have transient DbContext in all parts of the application, including Web API and it is not desired.
cc: @danroth27
@iAmBipinPaul thanks for contacting us.
You should use OwningComponentBase<T> in these situations. Below is an updated sample that shows the right pattern.
@Andrzej-W UI code is single threaded, we make no guarantees about anything else. The issue above is not with multiple threads, but with having asynchronous operations be performed simultaneously, which EF doesn't support.
What that means is that there are multiple parallel operations interleaved within one thread. We have a synchronization context to make sure of that.
Hi ,Thanks
@Andrzej-W , @javiercn
It does it means that we have to use OwningComponentBase<T> on each component we think that can go under pre-render.
What will be the effect of OwningComponentBase<T> , when rendering on client side.
so it will be synchronization on client side as well (where it will make HTTP call instead of working with EF Core).
It does it means that we have to use
OwningComponentBase<T>on each component we think that can go under pre-render.
No, only components that can't perform multiple operations asynchronously. Which means that they can't wait for multiple signals to resume execution. For example, in ef you can't do
var result1Task = context.Entity.Where(filter);
var result2Task = context.Entity.Where(filter);
await result1Task;
await result2Task.
The operations in this case happen simultaneously but not concurrently, meaning that at no point there are two or more threads running EF code, but that the thread is freed up to do other things after the first call yields the thread waiting for the response.
The limitation is that EF code can't handle a new operation until the current one is completely finished, but all the code is single threaded (we run inside a synchronization context to ensure so).
In JavaScript is single-threaded too, because there's only a single thread (but that might change in the future) but HttpClient doesn't have that limitation.
In JavaScript is single-threaded too, because there's only a single thread (but that might change in the future) but HttpClient doesn't have that limitation.
Yeah .
so our interface is
namespace EFCorePreRenderIssue.Shared.Interfaces
{
public interface IProductService
{
Task<List<ProductDto>> GetAsync();
}
}
On Server (EF core)
namespace EFCorePreRenderIssue.Server
{
public class ProductService : IProductService
{
private readonly EFCorePreRenderIssueDbContext _eFCorePreRenderIssueDbContext;
public ProductService(EFCorePreRenderIssueDbContext eFCorePreRenderIssueDbContext)
{
_eFCorePreRenderIssueDbContext = eFCorePreRenderIssueDbContext;
}
public async Task<List<ProductDto>> GetAsync()
{
return await _eFCorePreRenderIssueDbContext.Products.Select(p => new ProductDto
{
Id = p.Id,
Name = p.Name,
UnitId = p.UnitId,
Unit = new UnitDto
{
Name = p.Unit.Name,
Id = p.Unit.Id
}
}).ToListAsync();
}
}
}
On client
namespace EFCorePreRenderIssue.Client
{
public class ProductService : IProductService
{
private readonly HttpClient _httpClient;
private readonly string _baseUrl = "api/Product";
public ProductService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async Task<List<ProductDto>> GetAsync()
{
return await _httpClient.GetJsonAsync<List<ProductDto>>(_baseUrl);
}
}
}
and if we are using OwningComponentBase<T> on Product.razor
Where we are injection IProductService , as if you mention OwningComponentBase<T> will force to make synchronization call does it mean that on client side (Http call) , it will be synchronization only as we are using OwningComponentBase<IProductService> on Product.razor
Most helpful comment
@iAmBipinPaul thanks for contacting us.
You should use
OwningComponentBase<T>in these situations. Below is an updated sample that shows the right pattern.EFReproCorrectPattern.zip
@Andrzej-W UI code is single threaded, we make no guarantees about anything else. The issue above is not with multiple threads, but with having asynchronous operations be performed simultaneously, which EF doesn't support.
What that means is that there are multiple parallel operations interleaved within one thread. We have a synchronization context to make sure of that.