I have an async method, and from within that method I call another Async method.
In the second method I call an API. I know that my API request is correct, so it has something to do with the async/await.
Am I creating a deadlock? If so where? And how to fix it?
public async Task<AmountInvoicedModel> CreatePaymentsAndSendAsEmail(InvoiceRequestModel model, bool calculate)
{
....
await CreateQRCodes("testMsg");
....
}
public async Task CreateQRCodes(string ocrNmbr)
{
HttpClient client = new HttpClient();
client.BaseAddress = new Uri("https://mpc.getswish.net/qrg-swish/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
var json = new
{
payee = new
{
value = "01234567890",
editable = false
},
amount = new
{
value = 100,
editable = false
},
message = new
{
value = $"{ocrNmbr}",
editable = false
},
format = "jpg",
size = 300
};
try
{
HttpResponseMessage response = await client.PostAsJsonAsync(
"api/v1/prefilled", json);
var result = await response.Content.ReadAsStreamAsync();
}
catch (Exception ex)
{
throw;
}
}
UPDATE: I had to put await on the "CalculateInvoice" method too. So now it doesnt deadlock anymore, it moves on - but without giving me a response
[HttpPost]
[Route("calculateInvoice")]
public async Task<IHttpActionResult> CalculateInvoice([FromBody] InvoiceRequestModel model)
{
model.EmailAddress = AccountHelper.GetLoggedInUsername();
var result = await _paymentHandler.CreatePaymentsAndSendAsEmail(model, true);
if (result == null)
return Conflict();
return Ok(result);
}
I had to put await on the CalculateInvoice method for it to move on.
[HttpPost]
[Route("calculateInvoice")]
public async Task<IHttpActionResult> CalculateInvoice([FromBody] InvoiceRequestModel model)
{
model.EmailAddress = AccountHelper.GetLoggedInUsername();
var result = await _paymentHandler.CreatePaymentsAndSendAsEmail(model, true);
if (result == null)
return Conflict();
return Ok(result);
}
Related
In the below code for each request we are calling rest client in an Async manner. Rest client is basically a http client which is calling some webapis in a batch. If suppose AsyncTaskCount value is 5 then 5 request will be called asynchronously and then in the while block we are getting the result for each call. If any response out of those 5 request has an exception then the response will be faulted and IsFaulted becomes true for that particular request and in the response we can get the inner exception.
private async Task<List<RequestResponse>> ProcessInvestment(List<Request> Requests, List<Result> Results, ILogger log)
{
var requestResponses = new List<RequestResponse>();
var asyncTaskCount = Convert.ToInt32(Environment.GetEnvironmentVariable("AsyncTaskCount"));
log.LogInformation($"Start processing {Requests.Count} in batches of {asyncTaskCount}");
for (int index = 0; index < Requests.Count; index = index + asyncTaskCount)
{
var requestBatch = Requests.Skip(index).Take(asyncTaskCount).ToList();
var requests = requestBatch.Select(x => _restClient.RequestResponse(x)).ToList();
while (requests.Count > 0)
{
// Identify the first task that completes.
Task<RequestResponse> requestResponseTask = await Task.WhenAny(requests);
var requestResponse = new RequestResponse();
// ***Remove the selected task from the list so that you don't process it more than once
requests.Remove(requestResponseTask);
if (!requestResponseTask.IsFaulted)
{
// Await the completed task.
requestResponse = await requestResponseTask;
requestResponses.Add(requestResponse);
}
else
{
if (requestResponseTask.Exception.InnerException != null && requestResponseTask.Exception.InnerException is Exception)
{
var result = new Result();
result = ResponseTransformComponent.ResponseToResult(((Exception)requestResponseTask.Exception.InnerException).Request, null);
result.SetBadRequestErrorDetails(((Exception)RequestResponseTask.Exception.InnerException).BadRequestResponse);
results.Add(Result);
}
else
{
throw requestResponseTask.Exception;
}
}
}
log.LogInformation($"Number of records processed = {requestResponses.Count}");
}
log.LogInformation($"Total invalid and Bad requests count = {results.Count}");
return RequestResponses;
}
Below is the code for restclient which is called from the above method.
public async Task<Response> RequestResponse(Request request)
{
var response = await GetDataFromService("calculation", "CalculateCapital", request);
return JsonConvert.DeserializeObject<Response>(response);
}
public async Task<string> GetDataFromService(string controller, string method, object request)
{
var client = _httpClientFactory.CreateClient(ServiceEnum.DCS);
string baseAddress = client.BaseAddress.ToString();
var requestUrl = $"api/{controller}/{method}";
var content = new StringContent(JsonConvert.SerializeObject(request), Encoding.UTF8, "application/json");
var response = await client.PostAsync(requestUrl, content).ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
var responseResult = response.Content.ReadAsStringAsync().Result;
if (response.StatusCode == HttpStatusCode.BadRequest)
{
throw new CalculatorServiceException("Bad Request", JsonConvert.DeserializeObject<BadRequestResponse>(responseResult), (Request)request);
}
throw new Exception($"Status code: {response.StatusCode}. {responseResult}");
}
return response.Content.ReadAsStringAsync().Result;
}
GetDataFromService method is called from Calculate method. And GetDataFromService method will return a custom exception if the request is a Bad Request.
I am trying to write the unit test case for the above method and try to mock a request so that it will return a faulted task and then IsFaulted should become true. Below is the part of my unit test case.
_restClient
.When(a => a.RequestResponse(Arg.Any<Request>()))
.Do(a => {Task.FromException(new CalculatorServiceException(string.Empty, new BadRequestResponse { Message = string.Empty, ModelState = new Dictionary<string, string[]> { { "CalculationDates.StartDate", new string[] { "0002: Duration too short to execute calculations (CalculationDates.StartDate)" } } } }, Arg.Any<Request>())); });
If i mock my restclient method like above then it is throwing the exception instead of giving the response with IsFaulted to true. So how should i mock the restclient method so that it will return a faulted task which has an exception instead of throwing it. Please suggest.
Thanks in advance.
When..Do is for wiring up callbacks for when a member is called.
Try using Returns instead:
_restClient.RequestResponse(Arg.Any<Request>())
.Returns(x => Task.FromException<RequestResponse>(...));
// (assuming RequestResponse(..) returns a Task<RequestResponse>. Tweak as required)
I've been trying to figure out how to read the contents of a httpclient call, and I can't seem to get it. The response status I get is 200, but I can't figure out how to get to the actual Json being returned, which is all I need!
The following is my code:
async Task<string> GetResponseString(string text)
{
var httpClient = new HttpClient();
var parameters = new Dictionary<string, string>();
parameters["text"] = text;
Task<HttpResponseMessage> response =
httpClient.PostAsync(BaseUri, new FormUrlEncodedContent(parameters));
return await response.Result.Content.ReadAsStringAsync();
}
And I am getting it just calling it from a method:
Task<string> result = GetResponseString(text);
And This is what I get
response Id = 89, Status = RanToCompletion, Method = "{null}", Result = "StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:\r\n{\r\n Connection: keep-alive\r\n Date: Mon, 27 Oct 2014 21:56:43 GMT\r\n ETag: \"5a266b16b9dccea99d3e76bf8c1253e0\"\r\n Server: nginx/0.7.65\r\n Content-Length: 125\r\n Content-Type: application/json\r\n}" System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage>
Update: This is my current code per Nathan's response below
async Task<string> GetResponseString(string text)
{
var httpClient = new HttpClient();
var parameters = new Dictionary<string, string>();
parameters["text"] = text;
var response = await httpClient.PostAsync(BaseUri, new FormUrlEncodedContent(parameters));
var contents = await response.Content.ReadAsStringAsync();
return contents;
}
And I call it from this method....
string AnalyzeSingle(string text)
{
try
{
Task<string> result = GetResponseString(text);
var model = JsonConvert.DeserializeObject<SentimentJsonModel>(result.Result);
if (Convert.ToInt16(model.pos) == 1)
{
_numRetries = 0;
return "positive";
}
if (Convert.ToInt16(model.neg) == 1)
{
_numRetries = 0;
return "negative";
}
if (Convert.ToInt16(model.mid) == 1)
{
_numRetries = 0;
return "neutral";
}
return "";
}
catch (Exception e)
{
if (_numRetries > 3)
{
LogThis(string.Format("Exception caught [{0}] .... skipping", e.Message));
_numRetries = 0;
return "";
}
_numRetries++;
return AnalyzeSingle(text);
}
}
And it keeps running forever, It hits the line
var model = JsonConvert.DeserializeObject<SentimentJsonModel>(result.Result);
Once, and it continues to go without stopping at another breakpoint.
When I pause execution, It say
Id = Cannot evaluate expression because the code of the current method is optimized., Status = Cannot evaluate expression because the code of the current method is optimized., Method = Cannot evaluate expression because the code of the current method is optimized., Result = Cannot evaluate expression because the code of the current method is optimized.
.. I Continue execution, but it just runs forever. Not sure what the problem is
The way you are using await/async is poor at best, and it makes it hard to follow. You are mixing await with Task'1.Result, which is just confusing. However, it looks like you are looking at a final task result, rather than the contents.
I've rewritten your function and function call, which should fix your issue:
async Task<string> GetResponseString(string text)
{
var httpClient = new HttpClient();
var parameters = new Dictionary<string, string>();
parameters["text"] = text;
var response = await httpClient.PostAsync(BaseUri, new FormUrlEncodedContent(parameters));
var contents = await response.Content.ReadAsStringAsync();
return contents;
}
And your final function call:
Task<string> result = GetResponseString(text);
var finalResult = result.Result;
Or even better:
var finalResult = await GetResponseString(text);
If you are not wanting to use async you can add .Result to force the code to execute synchronously:
private string GetResponseString(string text)
{
var httpClient = new HttpClient();
var parameters = new Dictionary<string, string>();
parameters["text"] = text;
var response = httpClient.PostAsync(BaseUri, new FormUrlEncodedContent(parameters)).Result;
var contents = response.Content.ReadAsStringAsync().Result;
return contents;
}
I am using the default Web Api controller that is auto generated. I am testing the validation and error handling in the client side. But somehow I have realised that the detail error message is not passed to client. Either if I throw HttpResponseException or returning IHttpActionResult in both cases the client is seeing only "Bad Request" but not the detailed message. Can anyone explain what is going wrong please?
public IHttpActionResult Delete(int id)
{
if (id <= 0)
{
var response = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent("Id should be greater than zero.", System.Text.Encoding.UTF8, "text/plain"),
StatusCode = HttpStatusCode.NotFound
};
throw new HttpResponseException(response) // Either this way
}
var itemToDelete = (from i in Values
where i.Id == id
select i).SingleOrDefault();
if (itemToDelete == null)
{
return BadRequest(string.Format("Unable to find a value for the Id {0}", id)); // Or This way
}
Values.Remove(itemToDelete);
return Ok();
}
client code is as:
private async static Task DeleteValue(int id)
{
var url = "http://localhost:13628/api/Values/" + id;
using (var client = new HttpClient())
{
var response = await client.DeleteAsync(url);
if (response.IsSuccessStatusCode)
{
await ReadValues();
}
else
{
Console.WriteLine(response.ReasonPhrase);
Console.WriteLine(response.StatusCode);
}
}
}
None of the above works??
Thx
In your client side change Console.WriteLine(response.ReasonPhrase);
to Console.WriteLine(response.Content.ReadAsStringAsync().Result);
and it will give the detailed error message.
Replace below code into Web API delete action. Use HttpResponseMessage as return tpye for api instead of IHttpActionResult
[HttpDelete]
[Route("{id:int:min(1)}")]
public async Task<HttpResponseMessage> DeleteAsync(int id)
{
if(id < 0 )
{
return await Task.FromResult<HttpResponseMessage>(Request.CreateResponse<string>(HttpStatusCode.BadRequest, "Id should be greater than zero."));
}
try
{
var itemToDelete = (from i in Values
where i.Id == id
select i).SingleOrDefault();
if (itemToDelete == null)
{
return await Task.FromResult<HttpResponseMessage>(Request.CreateResponse<string>(HttpStatusCode.NotFound,
string.Format("Unable to find a value for the Id {0}", id)));
}
Values.Remove(itemToDelete);
return await Task.FromResult<HttpResponseMessage>(Request.CreateResponse(HttpStatusCode.OK));
}
catch (Exception ex)
{
return Request.CreateResponse<string>(HttpStatusCode.InternalServerError, "Something went wrong."); // Default message if exception occured
}
}
And client Side:
private async static Task DeleteValue(int id)
{
var url = "http://localhost:13628/api/Values/" + id;
using (var client = new HttpClient())
{
var response = await client.DeleteAsync(url);
if (response.IsSuccessStatusCode)
{
await ReadValues();
}
else
{
var errorMessage = Newtonsoft.Json.JsonConvert.DeserializeObject<string>(await response.Content.ReadAsStringAsync());
// Here Newtonsoft.Json Package is used to deserialize response content
Console.WriteLine(errorMessage);
Console.WriteLine(response.StatusCode);
}
}
}
Above code is working at my side.
I am working on bot technology, in one of my projet i wrote below lines of code
private async void DeliveryProgressReport(IDialogContext context, Activity message)
{
MessagesController.progressdetails = SQLDatabaseService.getinprogressdetails();
var progress = MessagesController.progressdetails;
if (progress.Count > 0)
{
try
{
Activity replyToConversation = message.CreateReply("**In Progress Report Details**");
replyToConversation.Recipient = message.From;
replyToConversation.Type = "message";
replyToConversation.Attachments = new List<Attachment>();
Dictionary<string, string> progresslist = new Dictionary<string, string>();
foreach (var progressreport in progress)
{
//Invoke the machine learning model for predicting the delivery status of delivery person
//var deliveryStatus= await InvokeRequestResponseServiceOfDeliveryPersonPredictionExp1();
//await Task.Delay(TimeSpan.FromSeconds(5));
var deliveryStatus = await InvokeRequestResponseServiceOfDeliveryPersonPredictionExp(progress[0].Name, progress[0].Mobile_Number);
progresslist.Add(progressreport.Name, progressreport.Mobile_Number);
List<CardImage> cardImages = new List<CardImage>();
cardImages.Add(new CardImage(url: progressreport.Photo_Url));
ThumbnailCard tlcard = new ThumbnailCard()
{
Title = "Name:" + progressreport.Name,
Subtitle = "Call:" + progressreport.Mobile_Number,
Images = cardImages,
Text = "Staus by Using Machine Learning Prediction:" + deliveryStatus
};
Attachment plAttachment = tlcard.ToAttachment();
replyToConversation.Attachments.Add(plAttachment);
}
replyToConversation.AttachmentLayout = AttachmentLayoutTypes.List;
await context.PostAsync(replyToConversation);
} catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
}
else
{
Activity replyToConversation = message.CreateReply("**There are no in progress deliveries are found**");
await context.PostAsync(replyToConversation);
}
}
private async Task<string> InvokeRequestResponseServiceOfDeliveryPersonPredictionExp(string name, string mobile_Number)
{
string status = "";
//Func<Stream, Task> copyStreamAsync = async stream =>
//{
//await Task.Factory.StartNew(async () =>
//{
//using (stream)
//using (var sourceStream = await sourceContent.Content.ReadAsStreamAsync())
//{
// await sourceStream.CopyToAsync(stream);
//}
//var client = new HttpClient();
using (var client = new HttpClient())
{
var scoreRequest = new
{
Inputs = new Dictionary<string, StringTable>() {
{
"input1",
new StringTable()
{
ColumnNames = new string[] {"Id", "Name", "Mobile_Number", "CourierCompany_Name", "Status", "EmailId"},
Values = new string[,] { { "", name, mobile_Number, "", "","" }, { "", name, mobile_Number, "", "", "" }, }
}
},
},
GlobalParameters = new Dictionary<string, string>()
{
}
};
const string apiKey = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx=="; // Replace this with the API key for the web service
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);
client.BaseAddress = new Uri("My Request URL");
// WARNING: The 'await' statement below can result in a deadlock if you are calling this code from the UI thread of an ASP.Net application.
// One way to address this would be to call ConfigureAwait(false) so that the execution does not attempt to resume on the original context.
// For instance, replace code such as:
// result = await DoSomeTask()
// with the following:
// result = await DoSomeTask().ConfigureAwait(false)
//var status = await PostRequest(scoreRequest,client).ConfigureAwait(false);
HttpResponseMessage response = await client.PostAsJsonAsync("", scoreRequest);//.ConfigureAwait(false);
string correctLocation = "";
string wrongLocation = "";
string notReached = "";
string personMismatch = "";
if (response.IsSuccessStatusCode)
{
string result = await response.Content.ReadAsStringAsync();
var results = JsonConvert.DeserializeObject<RootObject>(result);
foreach (var value in results.Results.output1.value.Values)
{
status = value[8].ToString();
correctLocation = value[4].ToString();
notReached = value[5].ToString();
personMismatch = value[6].ToString();
wrongLocation = value[7].ToString();
}
Debug.WriteLine("Result: {0}", result);
return status;
}
else
{
Debug.WriteLine(string.Format("The request failed with status code: {0}", response.StatusCode));
// Print the headers - they include the requert ID and the timestamp, which are useful for debugging the failure
Debug.WriteLine(response.Headers.ToString());
string responseContent = await response.Content.ReadAsStringAsync();
Debug.WriteLine(responseContent);
return status;
}
};
// return status;
}
After executing this below line I got the exception like asynchronous module or handler completed while an asynchronous operation was still pending
await context.PostAsync(replyToConversation);
Before posting this question I had followed through this below links, but I didn't resolve it.
Async Void, ASP.Net, and Count of Outstanding Operations
Web Api + HttpClient: An asynchronous module or handler completed while an asynchronous operation was still pending
Please tell how to resolve this exception.
-Pradeep
Finally, I resolved the above exception when I am return Task instead of void in DeliveryProgressReport method. and also where ever I was called the await DeliveryProgressReport() method there also I return Task instead of void.
-Pradeep
Following code has a bug: result doesn't contain any state; IsCompleted, IsCanceled and IsFaulted are always false, but I tested that Task works correctly, where is a problem?
var result = _dataService.SyncPoll(webApiPoll);
if (result.IsCompleted)
{
_logger.Info("Execute sync, poll was completed");
poll.IsSynchronized = true;
poll.ServerStatus = ServerStatus.Active;
ctx.SaveChanges();
}
//////
public Task SyncPoll(PollDto poll)
{
if (!_isAuthorized)
{
return null;
}
var client = new ApiClient(_baseApiUrl, _authToken);
Task result = Task.Run(async () => await client.SyncPollWeb(poll));
return result;
}
///////
public async Task<HttpResponseMessage> SyncPollWeb(PollDto poll)
{
HttpResponseMessage resp;
//System.Diagnostics.Debugger.Launch();
using (var client = GetClient())
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(_authType, _accessToken);
resp = await client.PostAsJsonAsync<PollDto>("api/poll", poll);
}
return resp;
}
what you are doing actually is a fire and forget so you don't have the status with such principle
The issue come from the await
you have to add await in order to have back the status
var result = await _dataService.SyncPoll(webApiPoll);
//and here
Task result = await client.SyncPollWeb(poll);
That's happened because when you checking the state your task is not finished (and running) yet - you just create it. You should wait until any state is deternined for your task. Use
result.Wait();
before checking for result.IsCompleted