I have a very minimal.NET 6.0 isolated, durable function.
[Function("SampleWorker")]
public async Task Run([ServiceBusTrigger("sighting-export", Connection = "MyServiceBus")] string queueMessage, [DurableClient] DurableTaskClient client)
{
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync("PrepareStuff");
}
I am injecting my repository in the constructor using DI
private readonly IMyRepository _repository;
public SampleWorker(IMyRepository repository)
{
_repository = repository;
}
I successfully invoke the Orchestration function which looks like this:
[Function("PrepareStuff")]
public async Task PrepareStuff([OrchestrationTrigger] TaskOrchestrationContext context)
{
User a = _repository.GetUser(1);
BlobServiceClient blobClient = new BlobServiceClient("SUPPRESSED");
BlobContainerClient container = blobClient.GetBlobContainerClient("img-species");
BlobClient blob = container.GetBlobClient("1.jpg");
var x = await blob.ExistsAsync();
User b = _repository.GetUser(1);
}
The second call to get a simple record from my repository, fails with:
'Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances. Object name: 'MyDbContext'.'
When debugging, I've pinpointed that once the await blob.ExistsAsync() line completes, the dbContext inside my injected repository instance becomes disposed and is therefore no longer available for the second database call.
I have isolated and reduced the function down into minimal offending code and am stumped.
My Program.cs looks like this:
static async Task Main(string[] args)
{
string connection = Environment.GetEnvironmentVariable("MyConnectionString", EnvironmentVariableTarget.Process);
string apiBaseUrl = Environment.GetEnvironmentVariable("ApiBaseUrl", EnvironmentVariableTarget.Process);
var host = new HostBuilder()
.ConfigureFunctionsWorkerDefaults()
.ConfigureOpenApi()
.ConfigureServices(services =>
{
services.AddLogging();
services.AddDbContext<MyDbContext>(options => options.UseLazyLoadingProxies().UseSqlServer(connection));
services.AddScoped<IMyRepository, MyRepository>();
Some things I tried:
- This exception occurs both running locally as well as on Azure in Test and Production
- Changing Repository or DbContext to Singleton resolves the issue, but obviously that's not a good solution
- Changing the Repository to AddTransient has no effect, dbContext still gets disposed
- If I move all the offending code up out of the Orchestration function and into the main Run function, the problem goes away.
Why is my dbContext being disposed inside the Orchestration function only when I'm simply awaiting a call to the Azure BlobClient which is definitely an awaitable Task and not an async void...
UPDATE: I pulled all of that code out of the Orchestrator function and moved it into an Activity function below it and the code runs flawlessly. So dbContext is only being disposed when this work is done within the orchestrator. I know you are not supposed to do much IO within orchestrators, is this the explanation for my issues? I.e. orchestrators will dispose objects much quicker or interfere with their normal lifecycle?