calling WCF method asynchronously - c#

I'm looking for feedback on the following code. I've read here that I should avoid use of async void.
So as a result I've implemented the following in a method..
foreach (var sample in returns)
{
_logger.Debug("Calling async method");
var resultFromMethodCall = CallMethodAsync(uploadReturn);
_logger.Debug("Continuing....");
}
async Task<Tuple<bool,long>> CallMethodAsync(Sample sampleReturn)
{
try
{
Service1Client client = new Service1Client();
Tuple<bool, long> results = await client.ValidateSampleReturnAsync(sampleReturn);
_logger.Debug("call to Sample Return validator completed for sample: {0}", results.Item2);
return results;
}
catch (Exception ex)
{
_logger.Error(ex, "Error occured while calling WCF service");
return new Tuple<bool, long>(false, sampleReturn.Id);
}
}
When I do nothing with the returned variable resultFromMethodCall, the logging indicates the all is working as I expect. However when I log out items from the variable resultFromMethodCall, it appears that its now running synchronously as it waits for the object to be returned from the call.
Am I missing something obvious here? Or am I completely misunderstanding how this works.

CallMethodAsync is correct.
If you don't await (or Wait) resultFromMethodCall execution will continue while that task is still running. Whether you should allow that depends on what you want to happen.

However when I log out items from the variable resultFromMethodCall it appears that its now running synchronously as it waits for the object to be returned from the call.
If you're using resultFromMethodCall.Result to get the items, then yes, it's blocking and running synchronously.
If you're using await resultFromMethodCall to get the items, then no, it's not running synchronously. However, it is running sequentially - meaning that the method will "pause" at the await and only continue when the resultFromMethodCall task completes.

Related

C# don't wait task to finish, but result needed

Simply I want to make an external call to external api which has SenMessageAsync(string message) method and i need to return my client Ok() without waiting it to finish. But on the backend side I need to response of SendMessageAsync() method for continue some process. I mean somehow I need to await it.
Example
try
{
//...
var response = await SendMessageAsync("test"); //dont wait the response, return Ok()
//do something with response
}
catch(MyException ex)
{
//Log
}
catch(Exception ex)
{
//Log
}
Update:
I am updating with one solution I found. Task.Run() (which called fire and forget). Maybe it helps someone. Thanks for answers.
Solution 1:
_ = Task.Run(() => SendMessageAsync(), task =>
{
//We can have exception here if SendMessageAsync() fails
var exception = task.InnerException.Exception;
//Log the exception
}, TaskContinuationOptions.OnlyOnFaulted)
You need a basic distributed architecture, as described on my blog. In summary:
A durable queue. Serialize the work to be done into this queue (e.g., { "operation" = "SendMessage", "data" = "test" }).
A separate backend processor that reads from that queue and does the actual work (e.g., SendMessageAsync(message.data)).
Your backend processor can await the call to SendMessageAsync and then do further processing.
You can use the Task.Run method to run the SendMessageAsync method asynchronously on a separate thread. You can then return Ok() to the client immediately, while the SendMessageAsync method continues to run in the background. To wait for the result of the SendMessageAsync method, you can use the await keyword within the Task.Run method.
Here's an example:
public async Task<IActionResult> MyAction()
{
// Return Ok() immediately to the client
_ = Task.Run(async () =>
{
// Run SendMessageAsync asynchronously on a separate thread
var result = await SendMessageAsync("My message");
// Continue processing using the result of SendMessageAsync
// ...
});
return Ok();
}

Using Task.WhenAll but each individual Task's success needs to be tracked

The original method is the inefficient foreach loop that awaits every job (which is an I/O bound network call):
foreach (var job in Jobs)
{
try
{
await DoJobAsync(job); //Pass job to external vendor API
job.Succeeded = true;
}
catch (Exception)
{
//do nothing, let loop continue
}
}
Now to improve performance, I want to use Task.WhenAll to process all jobs in a non-blocking manner.
However, we need to make sure that each job object only has that Succeeded property set to true if the DoJobAsync task does not throw exception.
If we do this:
await Task.WhenAll(Jobs.Select(j =>
{
var task = DoJobAsync(j);
j.Succeeded = true;
return task;
}));
My understanding is that if any job task ends up throwing exception, that property will still get toggled to true because each individual task is not awaited as it is created, making the code flow go right past it.
I know I can capture the Task returned by Task.WhenAll to have access to a list of all exceptions thrown, but I can't figure out a way to use them to trace back to the job that threw the exception.
How do I work around this issue?
You could use an async delegate as the selector of the Select operator:
await Task.WhenAll(Jobs.Select(async job =>
{
await DoJobAsync(job);
job.Succeeded = true;
}));
This way each job will be projected to a Task that will not be the original DoJobAsync(job) task, but instead a wrapper task that encapsulates the logic for updating the Succeeded property. This property will be updated immediately after the DoJobAsync(job) task completes successfully.
It is possible that multiple Job objects might have their Succeeded property updated in parallel. It depends on whether a SynchronizationContext is installed on the current thread.
Return the result true/false from the method DoJobAsync().
This way you can return the result directly.
await Task.WhenAll(Jobs.Select(j =>
{
return DoJobAsync(j);
}));

How to wait the result of async operations without await?

void A()
{
foreach (var document in documents)
{
var res = records.BulkWriteAsync(operationList, writeOptions); // res is Task<BulkWriteResult<JobInfoRecord>>
}
}
After foreach I would like to wait the result of all BulkWriteAsync, how to do this? I don't want to mark A() as async and do the following
await records.BulkWriteAsync(operationList, writeOptions);
Is it good solution?
void A()
{
var tasks = new List<Task<BulkWriteResult<JobInfoRecord>>>();
foreach (var document in documents)
{
var task = records.BulkWriteAsync(operationList, writeOptions);
tasks.Add(task);
}
Task.WaitAll(tasks.ToArray());
}
I call A() in try catch if I will mark public async void A() as async I never be in catch
Well, first you want a Task that represents all the operations. The simplest way to do this is with a bit of LINQ:
Task.WhenAll(documents.Select(i => records.BulkWriteAsync(...)));
Then, you ideally want to await that task. If that isn't possible, you can try
task.GetAwaiter().GetResult();
However, make sure that none of the tasks have thread affinity - that's a great way to get a deadlock. Waiting for a task on the UI thread while the task itself needs the UI thread is a typical example.
The whole point of await is that it allows you to handle asynchronous code as if it were synchronous. So from the outside, it appears as if you never left the method until you actually get to a return (or the end of the method). For this to work, however, your method must return a Task (or Task<T>), and the callee must await your method in turn.
So a code like this:
try
{
tasks = Task.WhenAll(documents.Select(i => ...));
await tasks;
}
catch (Exception ex)
{
// Handle the exception
}
will appear to run completely synchronously, and all exceptions will be handled as usual (though since we're using Task.WhenAll, some will be wrapped in AggregateException).
However, this isn't actually possible to handle with the way .NET and C# is built, so the C# compiler cheats - await is basically a return that gives you a promise of the result you'll get in the future. And when that happens, the control returns back to where the await left the last time. Task is that promise - if you use async void, there's no way for the callee to know what's happening, and it has no option but to continue as if the asynchronous method was a run-and-forget method. If you use async Task, you can await the async method and everything "feels" synchronous again. Don't break the chain, and the illusion is perfect :)

Task await fails

I am starting a HubConnection. First I get a new Instance of HubConnection,
afterwards I create a new IHubProxy with Name = "fileHub" so far so good.
My problem is located at (or in) the awaitable Function ContinueWith, I try to start the Connection, and write to the console wheater the start was successful or not.
The connection.Start() is successful and "Connected" is written to the console.
Code added after the Console.WriteLine("Connected") is executed without problems, too.
But the Task never finishes, so the Client Class calling the HandleSignalRAsync() Method waits for the completion unsuccessfully.
Adding a return; or task.dispose(); does not solve my issue.
public static async Task HandleSignalRAsync()
{
connection = new HubConnection("http://localhost:12754");
myHub = connection.CreateHubProxy("fileHub");
await connection.Start().ContinueWith(
task =>
{
if (task.IsFaulted)
{
var ex = task.Exception.GetBaseException();
Console.WriteLine("There was an error opening the connection: {0}", ex);
}
else
{
Console.WriteLine("Connected");
}
});
}
I call the Method with the TaskAwaiter in another Class of my Solution:
Functions.HandleSignalRAsync().GetAwaiter().GetResult();
calling it with:
Functions.HandleSignalRAsync().Wait();
does not work, too.
But the Task never finishes
Because both examples synchronously block, causing your code to deadlock. You shouldn't be blocking on async code.
You'll need to properly asynchronously wait on HandleSignalRAsync:
await Functions.HandleSignalRAsync().ConfigureAwait(false);
If you're already using async-await, there's no advantage to use a continuation style with ContinueWith, you can simply wrap the execution in a try-catch statement and await inside:
try
{
await connection.Start().ConfigureAwait(false);
Console.WriteLine("Connected");
}
catch (Exception e)
{
Console.WriteLine("There was an error opening the connection: {0}", e);
}

Async/Await in foreach with HTTPClient

I have a webservice that loads up some plugins (dlls) and calls their Process method. One of the plugins takes a list of members and ensures that they are all included in a MailChimp list.
Here is the code that adds the users to the MailChimp group.
private async Task AddMCUsers(List<Member> _memberList)
{
using (var http = new HttpClient())
{
var creds = Convert.ToBase64String(Encoding.ASCII.GetBytes("user:password");
http.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", creds);
string memberURI = string.Format(#"{0}lists/{1}/members", _baseURI, _memberGroupId);
var jss = new JavaScriptSerializer();
foreach (var user in _memberlist)
{
var _addStatus = "";
try
{
var content = jss.Serialize(new MCPost()
{
email_address = user.Email,
status = "subscribed",
merge_fields = new MCMergeFields()
{
FNAME = user.Firstname,
LNAME = user.Lastname
}
});
using(var result = await http.PostAsync(memberURI, new StringContent(content,Encoding.UTF8, "application/json")))
{
var resultText = await result.Content.ReadAsStringAsync();
if(result.IsSuccessStatusCode)
{
_addStatus = "Success";
var _returnedUser = jss.Deserialize<MCMember>(resultText);
//Store new user's id
user.ServiceId = _returnedUser.id;
}
else
{
_addStatus = "Fail";
}
}
}
catch {
_addStatus = "Error";
}
LogEvent("Add User - " + _addStatus, string.Format("Id: {0} - {1} {2} (Account: {3}) : {4}", user.Id, user.Firstname, user.Lastname, user.AccountId, user.Email));
}
}
}
In normal procedural code, this wouldn't be a problem. However, the only Post method available on the httpClient was PostAsync. Being fairly new to the async/await stuff, I'm not sure the ramifications on the rest of my code ... particularly as it relates to my attempt to reuse the httpClient instead of instantiating a new one for each http call.
I'm not sure what happens with await when its wrapped in a foreach like I have. Will I run into issues with reusing the httpClient to make repeated calls when running asynchronously?
My other question is, what is actually going to be returned. IOW, my understanding is that await returns a Task. However, here, I'm looping through the list and making multiple calls to await PostAsync. My method returns a Task. But which task gets returned? If my calling method needs to wait for completion before moving on, what does its call look like?
private void Process()
{
//Get List
var task = AddMCUsers(list);
task.Wait();
//Subsequent processing
}
I've read that you should use Async all the way. Does this mean my calling method should look more like this?
public async Task Process()
{
//Get list
...
await AddMCUsers(list);
//Other processing
}
Thanks to whatever help you can offer on this.
In normal procedural code, this wouldn't be a problem.
The whole point of async/await is to write asynchronous code in a way that looks practically identical to "normal" synchronous code.
Being fairly new to the async/await stuff, I'm not sure the ramifications on the rest of my code ... particularly as it relates to my attempt to reuse the httpClient instead of instantiating a new one for each http call.
HttpClient was intended to be reused; in fact, it can be used for any number of calls simultaneously.
I'm not sure what happens with await when its wrapped in a foreach like I have.
One way to think of it is that await "pauses" the method until its operation completes. When the operation completes, then the method continues executing. I have an async intro that goes into more detail.
Will I run into issues with reusing the httpClient to make repeated calls when running asynchronously?
No, that's fine.
IOW, my understanding is that await returns a Task.
await takes a Task. It "unwraps" that task and returns the result of the task (if any). If the task completed with an exception, then await raises that exception.
My method returns a Task. But which task gets returned?
The Task returned from an async method is created by the async state machine. You don't have to worry about it. See my intro for more details.
If my calling method needs to wait for completion before moving on, what does its call look like? ... I've read that you should use Async all the way. Does this mean my calling method should look more like this?
Yes, it should look like your second snippet:
public async Task ProcessAsync()
{
//Get list
...
await AddMCUsers(list);
//Other processing
}
The only thing I changed was the Async suffix, which is recommended by the Task-based Asynchronous Pattern.
in your code you should be fine with reusing the HttpClient. What async / await is allow the code to release the execution thread to prevent locking a cpu thread while waiting for the web response. It also releases control back to the caller. When releasing code back to the caller it means that if your Process function does not await your AddMCUsers, Process could finish before AddMCUsers (useful in fire and forget situations to not await a method).
What async/await do not do is affect the logical flow of an individual method. When you await an async web call the execution is paused and then resumed at the same point once the web call returns. There is also thread context tracking and the code resumes in the same context (ie. UI thread or background thread depending on the parent) by default, but this can be changed if needed.
At some point in your code you may want to have a method that blocks until your async code competes and that is where you will want your Task.Wait() call to block execution. If all you use is awaits then it is possible for your program to end before your task competes. See the code example below.
class Program
{
static void Main(string[] args)
{
Task waitForMe = Task.Run(() => waitAsync());
}
static async Task waitAsync()
{
await Task.Delay(5000);
}
}
in the sample with out a Task.Wait call to block the Main method the program will end before the 5 second wait is complete. Having a main method of the following will cause the program to wait for 5 seconds before exiting:
static void Main(string[] args)
{
Task waitForMe = Task.Run(() => waitAsync());
waitForMe.Wait();
}

Categories

Resources