I'm using Entity Framework Core with the Cosmos DB provider in Azure Functions, and when two HTTP-triggered functions are invoked simultaneously, I get an exception stating that the DbContext
was accessed from another thread.
Here’s my setup:
DbContext
is registered and configured using the AddDbContext()
method.UnitOfWork
class uses this DbContext
, and it's registered as a scoped
service.MediatR
RequestHandler
classes depend on this UnitOfWork
and are registered using the RegisterServicesFromAssembly()
method.Program.cs
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureServices(services =>
{
services.AddDbContext<MyDbContext>(options =>
{
options.UseCosmos(
"https://your-cosmosdb-uri",
"your-cosmosdb-key",
"your-database-name"
);
}, ServiceLifetime.Transient);
services.AddTransient<IUnitOfWork, UnitOfWork>();
services.AddMediatR(cfg =>
cfg.RegisterServicesFromAssembly(typeof(Program).Assembly)
);
})
.Build();
host.Run();
MyFunctions.cs
public class MyFunctions
{
private readonly IMediator _mediator;
public MyFunctions(IMediator mediator)
{
_mediator = mediator;
}
[FunctionName("MyFunctionOne")]
public async Task<IActionResult> RunOne(
[HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
CancellationToken cancellationToken)
{
var result = await _mediator.Send(new MyRequest(), cancellationToken);
return new OkObjectResult(result);
}
[FunctionName("MyFunctionTwo")]
public async Task<IActionResult> RunTwo(
[HttpTrigger(AuthorizationLevel.Function, "get"] HttpRequest req,
CancellationToken cancellationToken)
{
var result = await _mediator.Send(new MyRequest(), cancellationToken);
return new OkObjectResult(result);
}
}
UnitOfWork.cs
public class UnitOfWork : IUnitOfWork
{
private readonly MyDbContext _context;
private readonly MyRepository _repo;
public UnitOfWork(MyDbContext context)
{
_context = context;
_repo = new MyRepository(_context);
}
public MyRepository ProductRepository { get => _repo; }
}
MyRequestHandler.cs
public class MyRequestHandler : IRequestHandler<MyRequest, MyResponse>
{
private readonly IUnitOfWork _unitOfWork;
public MyRequestHandler(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public async Task<MyResponse> Handle(MyRequest request, CancellationToken cancellationToken)
{
// Perform operations using UnitOfWork or any other logic
await _unitOfWork.SaveChangesAsync(cancellationToken);
// Create and return the response
return new MyResponse
{
Result = $"Processed: {request.Data}"
};
}
}
Any insights into the cause of this threading issue?
I’ve explicitly tried both Scoped
and Transient
lifetimes in the AddDbContext()
call, but the problem persists.
The problem was caused by a middleware that was registered and added to the pipeline using a single extension method called inside the ConfigureFunctionsWorkerDefaults()
, it was not registered properly using the ConfigureServices()
method.
TLDR - register your dependencies exclusively inside the ConfigureServices()
call.