I have .Net core 3.1 Web api project. There is a method to call separate API for getting some values.
This is my code
Define instance variable
CancellationTokenSource tokenSource = new CancellationTokenSource();
Looping through my values. There is separate method and user call the method when he wants.
foreach (var no in nos)
{
BackgroundJob.Enqueue(() => webServiceCall(no, tokenSource));//loop and give jobs to hangfire
}
WebServiceCall implementation
public async Task webServiceCall(int no, CancellationTokenSource tokenSource)
{
using (HttpClient client = new HttpClient())
{
try
{
var result = await client.GetAsync("https://localhost:44359/api/process/" + no, tokenSource.Token);
if (result.IsSuccessStatusCode)
{
var val = await result.Content.ReadAsStringAsync();
System.Diagnostics.Debug.WriteLine("Returned Value : " + val);
//logic code
}
}
catch (TaskCanceledException ex)
{
System.Diagnostics.Debug.WriteLine("HTTP Get request canceled." + ex.Message);
}
}
}
This code works and I need to cancel above batch from separate API. I try something like this and it doesn't work.
[HttpGet("CancellingJob")]
public async Task HagfireCancel()
{
tokenSource.Cancel();
}
But when I call to this, all processes happens as usual and did not canceled.... Can any one help me to cancel hangfire jobs?
I used Hangfire 1.7.0 version.
Related
I have a strange behavior that I can't manage to explain.
In an async function, an awaited call blocks forever.
Note: it seams that the problem occurs since I moved from a console app to a Windows Form. (the call is called from the constructor of Form1().
_client is the HttpClient dotnet class.
public async Task GetConfigurationFile()
{
var stringContent = new StringContent(JsonConvert.SerializeObject(companyKey), Encoding.UTF8, "application/json");
HttpResponseMessage response = null;
// This call works and returns the respons after a few milliseconds
response = _client.PostAsync(_configurationFileEndpoint, stringContent).Result;
// The same awaited call block forever and never returns.
response = await _client.PostAsync(_configurationFileEndpoint, stringContent);
}
public Form1()
{
InitializeComponent();
_engine = new Engine();
}
public Engine()
{
// Logic similar to this.
Configuration configuration = null;
try
{
using (var reader = new StreamReader(Directory.GetCurrentDirectory() + "/configuration.json"))
{
configuration = Newtonsoft.Json.JsonConvert.DeserializeObject<Configuration>(reader.ReadToEnd());
}
}
catch (Exception ex)
{
// Something done
}
_apiCall = new PlatformCommunication(configuration);
if (configuration == null)
{
try
{
_apiCall.GetConfigurationFile().Wait();
}
catch (Exception exc)
{
}
}
}
You are doing this:
_apiCall.GetConfigurationFile().Wait();
As explained in many places, such as here - blocking on async code from UI thread is bad idea. When you have this:
response = await _client.PostAsync(_configurationFileEndpoint, stringContent);
the SynchronizationContext will be captured before await and execution after await will continue on that context, which means in this case on UI thread. It cannot continue there, because UI thread is blocked by GetConfigurationFile().Wait(), so you have deadlock.
When you have this:
response = _client.PostAsync(_configurationFileEndpoint, stringContent).Result;
The code inside PostAsync uses ConfigureAwait(false) on every async call, to prevent continuations running on captured context. So all continuations run on thread pool threads and you can get away with blocking on async call with Result in this case (doesn't make it good idea still). Then after this change your GetConfigurationFile becomes synchronous (there is no await left), so you can get away with Wait() also.
You can do the same ConfigureAwait(false):
response = await _client.PostAsync(_configurationFileEndpoint, stringContent).ConfigureAwait(false);
And it will help in this case, but that's not the way to solve this problem. The real way is to just not block on async code on UI thread. Move _apiCall.GetConfigurationFile() outside of constructor.
#YK1: to prevent blocking calls, I can move the code in the
constructor of Engine() to an Async Initialize function and await
_apiCall.GetConfigurationFile() instead of_apiCall.GetConfigurationFile().Wait(); But then in my Winform, I
need to await engine.Initialize() from an Async function which I don't
have? ( engine must run automatically, not be behind a start button),
reason why I put it in the constructor of the form which is not async.
Instead of constructor, move your startup code code to an async method. You can subscribe to Form_Load event and call that method.
class Engine
{
public async Task Init()
{
// Logic similar to this.
Configuration configuration = null;
try
{
using (var reader = new StreamReader(Directory.GetCurrentDirectory() + "/configuration.json"))
{
configuration = Newtonsoft.Json.JsonConvert.DeserializeObject<Configuration>(reader.ReadToEnd());
}
}
catch (Exception ex)
{
// Something done
}
_apiCall = new PlatformCommunication(configuration);
if (configuration == null)
{
try
{
await _apiCall.GetConfigurationFile();
}
catch (Exception exc)
{
}
}
}
}
and
private async void Form_Load(object sender, EventArgs e)
{
_engine = new Engine();
await _engine.Init();
}
Application : Asp.Net Core Web API
I have requirement, On receiving the notification I should do 2 independent tasks. notification gets triggered as apart of web hook.
call another service and then saves the response in log.
Since this is independent task I do not want to wait the caller until we receive response. So I thought to wrap this function in Task.Run(), so that it will be run on another thread available on thread pool.
I donot care wheather it fails or success, I just need to log the response.
Task.Run(() => ProcessThroughRestClient(this.httpContext.Request, this.cspgConfiguration.Value.ApacNotificationService, this.restClient, this.logger));
Saves the request object in DB for tracing.
Since I must save the notification, I made it as awaitable task.
await this.invoiceDbInstance.UpdateItemAsync(payment);
Below is the full code.
Main Method
public async Task<IActionResult> NotifyAsync()
{
this.logger.LogTrace("{CorrelationId} - notify async method has been called.", this.CorrelationId);
Task.Run(() => ProcessThroughRestClient(this.httpContext.Request, this.Configuration.Value.NotificationService, this.restClient, this.logger));
var request = this.GetDataFeedRequest(this.httpContext.Request);
if (request != null)
{
this.logger.LogInformation("{CorrelationId} - data feed invoked with the order reference {OrderReference}", request.OrderReference, this.CorrelationId);
Func<Invoice, bool> condition = x => x.SourceTransactionId == request.OrderReference;
var payment = this.invoiceDbInstance.GetItem(condition);
if (payment != null)
{
payment.TransactionStatus = request.IsSuccessStatusCode() ? TransactionStatus.Success : TransactionStatus.Failure;
payment.TransactionId = request.PaymentReference;
payment.UpdatedAt = DateTime.UtcNow.ToLongDateString();
await this.invoiceDbInstance.UpdateItemAsync(payment);
this.logger.LogInformation("{CorrelationId} - data feed updated order reference {OrderReference} updated in Invoice with {PaymentReference}", request.OrderReference, this.CorrelationId, request.PaymentReference);
}
else
{
this.logger.LogInformation("{CorrelationId} - Payment not found.", this.CorrelationId);
}
}
this.logger.LogTrace("{CorrelationId}- notify async method ended.", this.CorrelationId);
return new OkResult();
}
Http Call function which will be invoked through Task.Run()
private static HttpResponseMessage ProcessThroughRestClient(HttpRequest request, string url, IRestClient client, ILogger<PayDollarNotificationService> log)
{
try
{
log.LogTrace("Paydollar notify async ProcessThroughRestClient method has been called.");
var parameters = request.Form?.ToDictionary(x => x.Key, x => x.Value.ToString());
if (parameters.Any())
{
var response = client.PostAsync(url, RestClientHelper.BuildFormUrlEncodedContent(parameters)).Result;
log.LogInformation("sent request to {url}.", url);
log.LogInformation($"{url} response {response.ReadContent()?.Result}");
return response;
}
else
{
log.LogInformation("No form parameters found.");
return null;
}
}
catch (Exception ex)
{
log.LogError($"An error occured: {ex.Message} {ex}");
}
return null;
}
My question is, Is there any advantage using Task.Run() as above instead awitable task? or is it is going to be blocking thread?
It works fine when have one or two tasks however throws an error "A task was cancelled" when we have more than one task listed.
List<Task> allTasks = new List<Task>();
allTasks.Add(....);
allTasks.Add(....);
Task.WaitAll(allTasks.ToArray(), configuration.CancellationToken);
private static Task<T> HttpClientSendAsync<T>(string url, object data, HttpMethod method, string contentType, CancellationToken token)
{
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(method, url);
HttpClient httpClient = new HttpClient();
httpClient.Timeout = new TimeSpan(Constants.TimeOut);
if (data != null)
{
byte[] byteArray = Encoding.ASCII.GetBytes(Helper.ToJSON(data));
MemoryStream memoryStream = new MemoryStream(byteArray);
httpRequestMessage.Content = new StringContent(new StreamReader(memoryStream).ReadToEnd(), Encoding.UTF8, contentType);
}
return httpClient.SendAsync(httpRequestMessage).ContinueWith(task =>
{
var response = task.Result;
return response.Content.ReadAsStringAsync().ContinueWith(stringTask =>
{
var json = stringTask.Result;
return Helper.FromJSON<T>(json);
});
}).Unwrap();
}
There's 2 likely reasons that a TaskCanceledException would be thrown:
Something called Cancel() on the CancellationTokenSource associated with the cancellation token before the task completed.
The request timed out, i.e. didn't complete within the timespan you specified on HttpClient.Timeout.
My guess is it was a timeout. (If it was an explicit cancellation, you probably would have figured that out.) You can be more certain by inspecting the exception:
try
{
var response = task.Result;
}
catch (TaskCanceledException ex)
{
// Check ex.CancellationToken.IsCancellationRequested here.
// If false, it's pretty safe to assume it was a timeout.
}
I ran into this issue because my Main() method wasn't waiting for the task to complete before returning, so the Task<HttpResponseMessage> was being cancelled when my console program exited.
C# ≥ 7.1
You can make the main method asynchronous and await the task.
public static async Task Main(){
Task<HttpResponseMessage> myTask = sendRequest(); // however you create the Task
HttpResponseMessage response = await myTask;
// process the response
}
C# < 7.1
The solution was to call myTask.GetAwaiter().GetResult() in Main() (from this answer).
var clientHttp = new HttpClient();
clientHttp.Timeout = TimeSpan.FromMinutes(30);
The above is the best approach for waiting on a large request.
You are confused about 30 minutes; it's random time and you can give any time that you want.
In other words, request will not wait for 30 minutes if they get results before 30 minutes.
30 min means request processing time is 30 min.
When we occurred error "Task was cancelled", or large data request requirements.
Another possibility is that the result is not awaited on the client side. This can happen if any one method on the call stack does not use the await keyword to wait for the call to be completed.
Promoting #JobaDiniz's comment to an answer:
Do not do the obvious thing and dispose the HttpClient instance, even though the code "looks right":
async Task<HttpResponseMessage> Method() {
using (var client = new HttpClient())
return client.GetAsync(request);
}
Disposing the HttpClient instance can cause following HTTP requests started by other instances of HttpClient to be cancelled!
The same happens with C#'s new RIAA syntax; slightly less obvious:
async Task<HttpResponseMessage> Method() {
using var client = new HttpClient();
return client.GetAsync(request);
}
Instead, the correct approach is to cache a static instance of HttpClient for your app or library, and reuse it:
static HttpClient client = new HttpClient();
async Task<HttpResponseMessage> Method() {
return client.GetAsync(request);
}
(The Async() request methods are all thread safe.)
in my .net core 3.1 applications I am getting two problem where inner cause was timeout exception.
1, one is i am getting aggregate exception and in it's inner exception was timeout exception
2, other case was Task canceled exception
My solution is
catch (Exception ex)
{
if (ex.InnerException is TimeoutException)
{
ex = ex.InnerException;
}
else if (ex is TaskCanceledException)
{
if ((ex as TaskCanceledException).CancellationToken == null || (ex as TaskCanceledException).CancellationToken.IsCancellationRequested == false)
{
ex = new TimeoutException("Timeout occurred");
}
}
Logger.Fatal(string.Format("Exception at calling {0} :{1}", url, ex.Message), ex);
}
In my situation, the controller method was not made as async and the method called inside the controller method was async.
So I guess its important to use async/await all the way to top level to avoid issues like these.
I was using a simple call instead of async. As soon I added await and made method async it started working fine.
public async Task<T> ExecuteScalarAsync<T>(string query, object parameter = null, CommandType commandType = CommandType.Text) where T : IConvertible
{
using (IDbConnection db = new SqlConnection(_con))
{
return await db.ExecuteScalarAsync<T>(query, parameter, null, null, commandType);
}
}
Another reason can be that if you are running the service (API) and put a breakpoint in the service (and your code is stuck at some breakpoint (e.g Visual Studio solution is showing Debugging instead of Running)). and then hitting the API from the client code. So if the service code a paused on some breakpoint, you just hit F5 in VS.
I need to integrate an API into my development with a specific scenario called "Time Out Reversal" (TOR)
This means, succintly, to comply with the following requirements:
Initiate a request by invoking an endpoint
If a response is not received within a defined time out
start a reversal request by invoking another endpoint
While the requirements seems very clear to me, I really haven't found a way to implement it by using tasks.
For example, I know how to delay a task but I dont really know how to set a timeout in seconds for a started task
Any suggestion?
You can easily implement a timeout like so:
public async Task TimoutReversal() {
var timeout = TimeSpan.FromSeconds(10);
try {
//assumes HttpClient
Client.Timeout = timeout;
await Client.GetAsync(firstEndpoint);
} catch (OperationCanceledException ex) {
await Client.DeleteAsync(secondEndpoint);
}
}
//or
public async Task TimoutReversal() {
var timeout = TimeSpan.FromSeconds(10);
var firstTask = Client.GetAsync(firstEndpoint);
if (await Task.WhenAny(firstTask, Task.Delay(timeout)) == firstTask) {
//success
}else {
//failure
await Client.DeleteAsync(secondEndpoint);
}
}
Also see protected answer here on SO: Task with timeout
Another option is to pass the Token from a CancellationTokenSource created from a TimeSpan to the HttpClient calls.
For few hours I am struggling with async code in C# and I can't really get why is my code deadlocked.
So far I've red many articles and anything ringed the bell for me.
Hope you can help me.
Here is the code I am trying to run.
Main
Task.Run(async () =>
{
Task<EventDetailed[]> events = getDetailedEvents();
await events;
}).Wait();
getDetailedEvents:
static async Task<EventDetailed[]> getDetailedEvents()
{
...
EventDetailed[] result = await LoadDetailedEventsDetailsAsync(evnts).ConfigureAwait(false);
return result;
}
And the core of my problem.
LoadDetailedEventsDetailsAsync
async static Task<EventDetailed[]> LoadDetailedEventsDetailsAsync(Event[] events)
{
List<EventDetailed> detailed = new List<EventDetailed>();
List<Task<WebResponse>> responses = new List<Task<WebResponse>>();
List<Event> tasksWithStream = new List<Event>();
foreach (Event e in events)
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://...");
... some headers etc ...
e.Stream = httpWebRequest.GetRequestStreamAsync();
e.WebRequest = httpWebRequest;
tasksWithStream.Add(e);
}
foreach (var tsk in tasksWithStream)
{
try {
await tsk.Stream.ConfigureAwait(false);
using (var streamWriter = new StreamWriter(tsk.Stream.Result))
{
streamWriter.Write("...");
streamWriter.Flush();
streamWriter.Close();
}
responses.Add(tsk.WebRequest.GetResponseAsync());
}
catch (Exception ex)
{
Logger.mes("Failed to get event data.");
}
}
foreach (var response in responses)
{
try
{
await response.ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.mes("Failed to get event data.");
continue;
}
parseData.Add(ParseData(response));
}
A couple points:
First, it's important to note that you should almost never call .Wait (or .Result) on an async task - you should use await instead. One of the very few exceptions is in the Main method of a console app. The reason is that if you don't block the main thread, your program will simply exit prematurely.
Second, if you need to make multiple HTTP requests that do not depend on each other (i.e. request B does not need the results of request A), then there are huge performance gains to be had by executing them in parallel. Better still, you are not consuming a thread per request because the calls are asynchronous, i.e. they don't block a thread while waiting for a response, so the same thread can effectively fire off many concurrent requests.
I won't re-write your code, but I will suggest how I'd restructure it:
static void Main(string[] args)
{
// start all async tasks in parallel.
var tasks = GetEvents().Select(GetEventDetailsAsync);
// wait for them all to complete. normally you should use await instead of Wait,
// but you can't because you're in the main method of a console app.
Task.WhenAll(task).Wait();
}
static IEnumerable<Event> GetEvents()
{
// build a list of whatever metadata is needed to do your async work.
// do NOT do any actual async work here.
}
async static Task<EventDetailed> GetEventDetailsAsync(Event e)
{
// do all async work here, use await as needed,
// but only for one event (no loops).
}