While writing an Azure function, I established a simple repository pattern using the standard IoC for Functions.
However, when I ran the project up, I started getting the following error:
System.ObjectDisposedException: ‘Cannot access a disposed object. A common cause of this error is disposing a context 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, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Odd!
This was very unexpected. The path of code that led to this was a very basic Add, so I was confident that the object had not been intentionally disposed.
If a write caused this, then what about a read?
I tried reading from the context at the same point, and saw the same effect. Finally, I injected the DB Context into the function itself and called it from the first line of the function call, and it worked fine.
In fact, the issue was that I was trying to read from an Azure Service Bus here:
var client = new QueueClient(connectionStringBuilder, ReceiveMode.PeekLock);
client.RegisterMessageHandler(handler, new MessageHandlerOptions(onException));
Before we continue, I realise that someone reading this will probably announce loudly: “You’re doing it wrong! You should use the Function Binding to read from a Service Bus Queue!” To that person, I reply loudly, and proudly: “Yes, you’re right - I’m doing it wrong!”
Back to the story. So, after I left, I saw … oops - wrong story.
The issue here is that the event is losing the context of the function, because the function can complete before the event fires.
However, it seems that I have solved this problem before. To fix this, you can simply use a TaskCompletionSource to set something up similar to the following:
private static TaskCompletionSource<bool> \_tcs;
[FunctionName("MyFunction")]
public async Task Run([TimerTrigger("0 \*/1 \* \* \* \*")]TimerInfo myTimer)
{
\_tcs = new TaskCompletionSource<bool>();
var client = new QueueClient(connectionStringBuilder, ReceiveMode.PeekLock);
client.RegisterMessageHandler(handler, new MessageHandlerOptions(onException));
await \_tcs.Task;
}
private Task handler(Message message, CancellationToken cancellationToken)
{
\_myService.AddData(someData);
\_tcs.SetResult(true);
. . .
}
Summary
As stated, if your problem is that you’re trying to use the service bus client, then you are probably doing it wrong. It’s something of a shame that the new Service Bus SDK doesn’t support a GetNextMessage() style syntax, but it doesn’t, and so you get forced into the Function Bindings (not that I have any specific issue with them).
However, there are many reasons why you may need or choose to use an event inside a function, and if you do, this is one possible way to do so.
It’s worth pointing out that the above solution will only work for the first call of the event. If you have multiple calls then you’ll need to somehow determine when the last one has occurred.
References
https://www.pmichaels.net/2020/09/19/creating-and-using-an-azure-service-bus-in-net-core/