0

Has anyone tried the following Microsoft quickstart "Connect Azure Functions to Azure Storage using Visual Studio Code" with Isolated worker model? https://learn.microsoft.com/en-gb/azure/azure-functions/functions-add-output-binding-storage-queue-vs-code?pivots=programming-language-csharp&tabs=isolated-process

I have created an async version and it works fine initially but then, after submitting 4 - 5 requests (by adding messages to the queue) got the following error and the function stops inserting messages to the queue:

[2024-12-08T05:54:01.909Z] Executed 'Functions.HttpTrigger3' (Failed, Id=d7c6cdb1-63a8-4c53-9e1f-abb7b6572ec4, Duration=70ms)
[2024-12-08T05:54:01.912Z] System.Private.CoreLib: Exception while executing function: Functions.HttpTrigger3. System.Private.CoreLib: Result: 
Failure
Exception: System.ObjectDisposedException: IFeatureCollection has been disposed.
[2024-12-08T05:54:01.915Z] Object name: 'Collection'.
[2024-12-08T05:54:01.916Z]    at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.ThrowContextDisposed()
[2024-12-08T05:54:01.917Z]    at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.ContextDisposed()
[2024-12-08T05:54:01.918Z]    at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.Fetch[TFeature](TFeature& cached, Func`2 factory)      
[2024-12-08T05:54:01.919Z]    at Microsoft.AspNetCore.Http.DefaultHttpResponse.get_StatusCode()
[2024-12-08T05:54:01.920Z]    at Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.AspNetCoreHttpResponseData.get_StatusCode() in /mnt/vss/_work/1/s/extensions/Worker.Extensions.Http.AspNetCore/src/HttpDataModel/AspNetCoreHttpResponseData.cs:line 39
[2024-12-08T05:54:01.921Z]    at Microsoft.Azure.Functions.Worker.Rpc.RpcExtensions.ToRpcHttpAsync(HttpResponseData response, ObjectSerializer 
serializer) in D:\a\_work\1\s\src\DotNetWorker.Grpc\RpcExtensions.cs:line 88
[2024-12-08T05:54:01.922Z]    at Microsoft.Azure.Functions.Worker.Rpc.RpcExtensions.ToRpcAsync(Object value, ObjectSerializer serializer) in D:\a\_work\1\s\src\DotNetWorker.Grpc\RpcExtensions.cs:line 35
[2024-12-08T05:54:01.923Z]    at Microsoft.Azure.Functions.Worker.Handlers.InvocationHandler.InvokeAsync(InvocationRequest request) in D:\a\_work\1\s\src\DotNetWorker.Grpc\Handlers\InvocationHandler.cs:line 102
Stack:    at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.ThrowContextDisposed()
[2024-12-08T05:54:01.924Z]    at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.ContextDisposed()
[2024-12-08T05:54:01.924Z]    at Microsoft.AspNetCore.Http.Features.FeatureReferences`1.Fetch[TFeature](TFeature& cached, Func`2 factory)
[2024-12-08T05:54:01.925Z]    at Microsoft.AspNetCore.Http.DefaultHttpResponse.get_StatusCode()
[2024-12-08T05:54:01.926Z]    at Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.AspNetCoreHttpResponseData.get_StatusCode() in /mnt/vss/_work/1/s/extensions/Worker.Extensions.Http.AspNetCore/src/HttpDataModel/AspNetCoreHttpResponseData.cs:line 39
[2024-12-08T05:54:01.927Z]    at Microsoft.Azure.Functions.Worker.Rpc.RpcExtensions.ToRpcHttpAsync(HttpResponseData response, ObjectSerializer serializer) in D:\a\_work\1\s\src\DotNetWorker.Grpc\RpcExtensions.cs:line 88
[2024-12-08T05:54:01.928Z]    at Microsoft.Azure.Functions.Worker.Rpc.RpcExtensions.ToRpcAsync(Object value, ObjectSerializer serializer) in D:\a\_work\1\s\src\DotNetWorker.Grpc\RpcExtensions.cs:line 35
[2024-12-08T05:54:01.929Z]    at Microsoft.Azure.Functions.Worker.Handlers.InvocationHandler.InvokeAsync(InvocationRequest request) in D:\a\_work\1\s\src\DotNetWorker.Grpc\Handlers\InvocationHandler.cs:line 102.

After a couple of minutes the function app recovers, but then after submitting several more requests got the error again.

Source code is listed below. I found the problem is related to the MultiResponse as if I change it to write to the queue only or just return HTTP response it works fine. The In-process Model version works perfect when sending to the queue and returning HTTP response.

namespace HttpTriggerAppVSC
{
    public class HttpExampleVSC
    {
        private readonly ILogger<HttpExampleVSC> _logger;

        public HttpExampleVSC(ILogger<HttpExampleVSC> logger)
        {
            _logger = logger;
        }
    
        [Function("HttpTrigger3")]
        public async Task<MultiResponse> HttpTrigger3(
            [HttpTrigger(AuthorizationLevel.System, "get", "post")] HttpRequestData req,
            FunctionContext executionContext, CancellationToken cancellationToken)
        {
            var logger = executionContext.GetLogger("HttpExampleVSC");
            logger.LogInformation("C# HTTP trigger function processed a request.");
    
            string name = req.Query["name"];
    
            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
            dynamic data = JsonConvert.DeserializeObject(requestBody);
            name = name ?? data?.name;
    
            string message = string.IsNullOrEmpty(name)
                ? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
                : $"Hello, {name}. This HTTP triggered function executed successfully.";
    
    
            //if (cancellationToken.IsCancellationRequested)
            //{
            //    return null;
            //}
            
            var response = req.CreateResponse(HttpStatusCode.OK);
            response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
            await response.WriteStringAsync(message);
    
            // Return a response to both HTTP trigger and storage output binding.
            return new MultiResponse()
            {
                // Write a single message.
                Messages = new string[] { message },
                HttpResponse = response
            };
        }
    }
    
    public class MultiResponse
    {
        [QueueOutput("outqueue",Connection = "AzureStorageAccount")]
        public string[] Messages { get; set; }
        public HttpResponseData HttpResponse { get; set; }
    } 
}

2
  • Instead of directly using the HttpResponse after creating it, ensure that you're writing to the response and returning it properly so it doesn't get disposed. Commented Dec 9, 2024 at 3:04
  • Thank you for your reply. If you could share some code that would be great. If I return HttpResponse only then I can't add message to the queue. Problem is that unlike In-Proc model here we have one combined MultiResponse (both Http response and add message to the queue). That's how Microsoft has shown in their example. I don't know another way for Isolated worker model. Commented Dec 9, 2024 at 10:33

1 Answer 1

1
  • The System.ObjectDisposedException error occurs due to the behavior of the Azure Functions Isolated Worker model, when attempting to combine an HTTP response and a queue output within a single return object, like MultiResponse.

By using below function code successfully send the multiple messages into the queue.

Function code:

public class Function1
{
    private readonly ILogger<Function1> _logger;
    private readonly string _queueName = "firstqueue";

    public Function1(ILogger<Function1> logger)
    {
        _logger = logger;
    }

    [Function("Function1")]
    public async Task<HttpResponseData> Run(
        [HttpTrigger(AuthorizationLevel.Function, "post", "get")] HttpRequestData req,
        FunctionContext context)
    {
        var logger = context.GetLogger("HttpTriggerWithQueue");
        logger.LogInformation("C# HTTP trigger function processed a request.");

        // Parse the request body to handle multiple messages
        string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
        var messages = JsonConvert.DeserializeObject<List<string>>(requestBody) ?? new List<string>();

        if (!messages.Any())
        {
            var errorResponse = req.CreateResponse(System.Net.HttpStatusCode.BadRequest);
            await errorResponse.WriteStringAsync("Request body must contain an array of messages.");
            return errorResponse;
        }

        string connectionString = Environment.GetEnvironmentVariable("AzureWebJobsStorage1");
        QueueClient queueClient = new QueueClient(connectionString, _queueName);
        await queueClient.CreateIfNotExistsAsync();

        var failedMessages = new List<string>();

        // Send each message to the queue
        foreach (var message in messages)
        {
            try
            {
                await queueClient.SendMessageAsync(message);
            }
            catch (Exception ex)
            {
                logger.LogError($"Failed to send message '{message}' to queue: {ex.Message}");
                failedMessages.Add(message);
            }
        }

        // Create the HTTP response
        var response = req.CreateResponse(System.Net.HttpStatusCode.OK);
        response.Headers.Add("Content-Type", "application/json");

        if (failedMessages.Any())
        {
            var errorDetails = new
            {
                success = false,
                failedMessages
            };
            await response.WriteStringAsync(JsonConvert.SerializeObject(errorDetails));
        }
        else
        {
            var successDetails = new
            {
                success = true,
                message = $"{messages.Count} messages processed successfully."
            };
            await response.WriteStringAsync(JsonConvert.SerializeObject(successDetails));
        }

        return response;
    }
}

Output:

enter image description here

enter image description here

enter image description here

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks Pavan, that works all good now! It is strange why Microsoft example (with MultiResponse) did not work.. The difference between their example and my code was: + Their function was static (I’ve also tried that) + Their function was making synchronous calls (did not tried that as from my understanding it requires changing the function app set up) Unless Microsoft example has used some older .NET version (I have been using .NET 8). Just wondering if this error has anything to do with the Function App service lifetimes: Transient, Scoped, Singleton.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.