BrokeredMessage being disposed after await - c#

I'm trying to use a service bus Azure function, where I accept a BrokeredMessage, then perform http requests, and then afterward decide whether to complete, abandon, or dead letter the message. But I've been finding the BrokeredMessage is being disposed early if I await an http request. It's throwing System.ObjectDisposedException: 'BrokeredMessage has been disposed.' if I try to use it at all.
Example:
public static async void Run(BrokeredMessage message, TraceWriter log)
{
var httpClient = new HttpClient()
{
BaseAddress = new Uri("http://google.com")
};
httpClient.DefaultRequestHeaders.Accept.Clear();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var request = new HttpRequestMessage(HttpMethod.Get, "/");
HttpResponseMessage response = await httpClient.SendAsync(request);
message.DeadLetter(); //Throws exception
}
I'm not sure if I'm doing something wrong or missing something but I can't figure out how to make any actions I need to do with the message after the await work correctly.

You shouldn't call Complete() explicitly. Azure Functions runtime will complete the message if the function finishes successfully or abandon if the function fails.
The Functions runtime receives a message in PeekLock mode and calls Complete on the message if the function finishes successfully, or calls Abandon if the function fails.
from docs

The issue was the function needs to return Task instead of void for the async to work correctly. After making that change it awaits correctly.

Related

HttpClient.PostAsync continueWith not executing

I need some help figuring out why the following code in the continueWith block is not being executed for a long running service call.
public static async void postServiceAsync(string json, string postServiceUrl, string callbackUrl, string clientId,
string tenant, string secret, string d365Environment, TraceWriter log)
{
HttpClient client = new HttpClient();
//Get authorization header
string authHeader = await D365Authorization.getAccessToken(clientId, tenant, secret, d365Environment);
client.DefaultRequestHeaders.Add("Authorization", authHeader);
var httpContent = new StringContent(json);
client.Timeout = TimeSpan.FromMinutes(90);
client.PostAsync(postServiceUrl, httpContent).ContinueWith(async (result) =>
{
//call callback URL
//This is not executed after a long running service that runs for 20 minutes.
}
}
The continueWith code does get run if the service execution time is short though. I thought it was a timeout issue so I added the client.Timeout value. I tried calling the service in Postman and a value is returned even after waiting for 20+ minutes. I am not using await as I want the execution to continue after calling PostAsync. I just want the continueWith callback executed after the long running service execution has completed. Thanks for your help!
The above method called postServiceAsync is called from an Azure function which is being called from an Azure Logic App http webhook action. Here is the Azure function:
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
...
PostServiceAsync.postServiceAsync(json, shipServiceUrl, callbackUrl, clientId, tenant, secret, d365Environment, log);
var resp = req.CreateResponse(HttpStatusCode.Accepted);
return resp;
}
}
From the Azure function, I need to return the Accepted status code right away. After I've finished calling the long running service using PostAsync, I need to post to the callback URL, which is what I am doing in the continueWith block. Like I mentioned, it works if the service runtime is short. I tried Camilo's suggestion of adding await but the continueWith code did not get executed. I also tried getting rid of the continueWith and just added the code after "await client.PostAsync(...)".
It turns out that there is an Azure function 230 second timeout for http calls without a response. I might not be able to use an Azure function for my purposes.

HTTP Call stuck on WPF

I am developing a WPF application where I am consuming some HTTP API to get data, I am using System.Net.Http.HttpClient class to make API calls, but as soon I call GetAsync method of the client I don't get any response back from the API and the debugger just goes away.
Below is the line I am using to make the call where the debugger gets lost
var response = await client.GetAsync(endpoint);
I don't know why I am not getting any response from the API.
Note: The API is working on browser as it is a simple GET call.
Try following
var response = await client.GetAsync(endpoint).ConfigureAwait(false);
Making the HTTP call inside Task.Run worked for me, I assume that this has something to relate with UI Thread blocking.
Thanks everyone for the help!!
var task = Task.Run(() => {
var response = await client.GetAsync(endpoint);
});
await task;

Using the HttpClient how can I view the request content body?

I'd like to see what the request body of my HTTP request contained for debugging purposes. Here's what I've got right now:
var httpResponseMessage = await _httpClient.PostAsync(uri, objectToInsert, jsonFormatter);
//This throws an exception
var thisDoesntWork = await httpResponseMessage.RequestMessage.Content.ReadAsStringAsync();
This throws an ObjectDisposedException. How can I view the request body to make sure the JSON being sent is correct?
The short answer is you can't after the request has already been sent. HttpClient disposes the content body after the request is made as a convenience to the developer. See Why do HttpClient.PostAsync and PutAsync dispose the content? for some detail.
As an alternative, you could inherit from an existing HttpContent implementation, override the Dispose method, and log the body of the content prior to disposal.
Or, as suggested, use an external tool to monitor the request in flight.
You'd need to first create a HttpRequestMessage to pass to the HttpClient. Then you can just evaluate/log the content.
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, uri);
request.Content=JsonSerializer.Serialize(objectToInsert);
Console.WriteLine(request.Content.ReadAsStringAsync().Result);
HttpResponseMessage response = await httpClient.SendAsync(request);
ObjectDisposedException is thrown because you're disposing the HttpRequestMessage and HttpClient before Content.ReadAsStringAsync() finishes.
Note that Content.ReadAsStringAsync() is an asynchronous method. You need to wait for it to complete before disposing the HttpClient.
If you are not wanting to use async you can add .Result to force the code to execute synchronously:
var response = httpClient.PostAsync(BaseUri, new FormUrlEncodedContent(parameters)).Result;
var contents = response.Content.ReadAsStringAsync().Result;

Why is the original task canceled when it ContinueWith something else?

It's been 4 weeks since I dived into C# programming. It's really fun, however, I got a pain in the ass:
When I start a Task with HttpClient.PostAsync() alone, it works fine. But if I continue with something else, the orginal Task will be canceled, not by me. Looks like the Task is not happy about being continued.
Task<HttpResponseMessage> task0;
Task task1;
using (var client = new HttpClient())
{
HttpContent content = new ByteArrayContent(new byte[]{});
task0 = client.PostAsync("<valid http address>", content);
task1 = task0.ContinueWith((t) =>
{
// Do nothing
});
}
task1.Wait();
// I got task0.IsCanceled == true here
I tried:
1, Add task0.wait() immediately after PostAsync() will solve the issue but it's not what I want. Because I need the performance benefit from async and doing that will make it totally sync.
2, Add task0.wait() before task1.wait() will cause a TaskCanceledExcpetion.
3, Remove task1 and wait on task0 will be OK.
4, Call task0.start() will got "Start may not be called on a promise-style task."
So, plz someone tell me what am I doing wrong?
PS:
Before I asked this question, I had googled it for days. And some questions from StackOverflow might look relevent, but it turned out they were not the same to mine.
Who canceled my Task? She/He was asking why the continuation task wasn't executed.
Why does TaskCanceledException occur? She/He was messing up with the CancellationToken which I never did and got unexpected result.
I've also read this Task Cancellation and still got no clue.
Your HttpClient most likely get's disposed before PostAsync is finished. Remove using statement for test purposes, and everything will work as expected. You should dispose your client at a different point, when request is finished.
Also, many would recommend reuse single instance of HttpClient as much as possible, if your application logic allows it.
So, this code appears to be disposing the HttpClient as soon as you hook up the continuation, which is most likely not what you want.
If you want to use a using statement to dispose your client instance, you need to use the async and await keywords. The following code is equivalent to your example, with the compiler hooking up the continuation for you.
public async Task FooAsync()
{
using (var client = new HttpClient())
{
HttpContent content = new ByteArrayContent(new byte[]{});
await client.PostAsync("<valid http address>", content);
// Continue your code here.
}
}
If you want to continue using continuations created without the compiler's help, you can put the disposing logic in a continuation:
Task<HttpResponseMessage> task0;
Task task1;
var client = new HttpClient();
HttpContent content = new ByteArrayContent(new byte[]{});
task0 = client.PostAsync("<valid http address>", content);
task1 = task0.ContinueWith((t) =>
{
client.Dispose();
})
task1.Wait();

Does HttpClient spin a new thread even if I immediately get the Result of the returned task?

In my application I like to try and be consistant and use HttpClient whenever I can. However, sometimes I dont need the asynchronous properties of HttpClient and so I simply get the Result of the Task as soon as it is returned as demonstrated in the code below.
public HttpResponseMessage httpPostWrapperMethod(string postBody, string url)
{
HttpContent content = new StringContent(postBody, Encoding.UTF8, "application/json");
HttpClient client = new HttpClient();
return client.PostAsync(url, content).Result;
}
My two part question is this:
Does this code cause a new thread to be spun in the background when making the call?
and
If my calls to this other service are taking around 500ms, is this going to cause me to eat up too many threads when the service is under a production load of around 100 requests/second?
PostAsync doesn't result in a thread being created, no. In fact, were the code to be used asynchronously no thread would ever need to be doing anything for this work to be done. In your case here you're having the current thread sit around doing nothing while you wait for the operation to finish, so that is the only thread who's time is being consumed/wasted when performing this operation.

Categories

Resources