I have got this code:
public async Task DoRespond(AspNetWebSocketContext context)
{
System.Net.WebSockets.WebSocket socket = context.WebSocket;
while (true)
{
ArraySegment<byte> buffer = new ArraySegment<byte>(new byte[1024]);
WebSocketReceiveResult result = await socket.ReceiveAsync(buffer, CancellationToken.None);
if (socket.State == WebSocketState.Open)
{
string userMessage = Encoding.UTF8.GetString(buffer.Array, 0, result.Count);
userMessage = "Message from client : " + userMessage;
buffer = new ArraySegment<byte>(Encoding.UTF8.GetBytes(userMessage));
await socket.SendAsync(buffer, WebSocketMessageType.Text, true, CancellationToken.None);
}
else
{
break;
}
}
I need to call this async method in different class in bool method (it is an NUnit framework)
protected override bool Test()
{
Websocket ws = new Websocket();
ws.ProcessRequest(context);
Thread.Sleep(1000);
logger.Write("Async method ");
var task = Task.Run(DoRespond);
}
I need to call async Task method in this bool method. How i can do that ? I aslo need call a parametre AspNetWebSocketContext context.
The async..await pattern is contagious and will spread thru your code base.
in order to call the async method, you need to await it in another async method
protected override async Task<bool> Test()
{
using (Websocket ws = new Websocket()) // properly dispose of WebSocket
{
ws.ProcessRequest(context);
await Task.Delay(1000); // notice the awaitable Delay replacing the blocking Sleep.
logger.Write("Async method ");
await DoRespond(context);
}
return true; // not sure where Boolean return value comes from as it wasn't in original method.
}
and, of course, whatever is calling Test() will get similar treatment.
Edit after more information in comments
The test method can be forced to wait for the async method to complete similar to this
protected override bool Test()
{
using (Websocket ws = new Websocket()) // properly dispose of WebSocket
{
ws.ProcessRequest(context);
Thread.Sleep(1000);
logger.Write("Async method ");
var task = DoRespond(context);
task.Wait(); // wait for async method to complete
// assert something?
}
return true; // not sure where Boolean return value comes from as it wasn't in original method.
}
However, do read up on asynchronous testing with NUnit since async test methods (like the first example) have been supported for several years.
For further reading, Async Support in NUnit
Related
I have below method in my ApiController,
public Task<HttpResponseMessage> Post([FromBody] CardParameters paras)
{
int amount = Convert.ToInt32(paras.Amount);
int cashout = Convert.ToInt32(paras.CashOut);
var promise = new TaskCompletionSource<HttpResponseMessage>();
void getResponse(string s)
{
promise.SetResult(Request.CreateResponse(s)); // this line executes in 2 seconds
}
AdpResponse ar = getResponse;
ta.purchase(amount, cashout, ar);
return promise.Task;
}
"ta.purchase" method is in a 3rd party app. That app will call the "getResponse" method at the end of process.
Client app receive the result on first call to this method.
But from the second call the client receives error "[InvalidOperationException: An asynchronous module or handler completed while an asynchronous operation was still pending.]
I resolved the issue by wrapping the entire process with a Task.
This article helped me to understand the concept.
The setResult(s) complete the task but it does not de-register the task from ASP.net. hence ASP.net throw an error when it try to exit from the method. So I had to use Task. This worked.
public Task<HttpResponseMessage> Post([FromBody] CardParameters paras){
int amount = Convert.ToInt32(paras.Amount);
int cashout = Convert.ToInt32(paras.CashOut);
string myResult ="";
var _t = Task.Run(async () =>
{
var promise = new TaskCompletionSource<HttpResponseMessage>();
void getResponse(string s)
{
promise.SetResult(s);
}
AdpResponse ar = getResponse;
ta.purchase(amount, cashout, ar);
myResult = promise.Task.Result;
});
await _t;
return Request.CreateResponse(myResult);
}
I am creating a messaging system and I am facing a problem. I need to Publish a message and wait for the response before returning the Publish function.
This is what my functions look like
public async Task<bool> Publish(int ClientId, string msg){
...
// Wait and check if the client the message was sent to respond
// if that does not happen within 5 seconds, return false, else true
}
private async Task MessageIntercept(int ClientId, string msg){
// Intercepts all messages
...
}
Both of these functions are on the server and the MessageIntercept task is automatically run whenever a message is sent (including the one sent with the Publish method). I can send a message from my asp.net website project by calling on the server project's Publish function mentioned above
Basically what i want to be able to do is call bool Success = Publish(1,"This is a test") and be able to determine whether or not the message was successfully sent, that the client understood and revived the message within 5 seconds.
This is what happens step by step:
I send a message from the server to the device with Publish
The message is intercepted by the MessageIntercept method (which I do not really care about, but the code is written so that all messages are intercepted)
The client receives and handles the message
The client responds, and the message is intercepted in MessageIntercept which is where I would like to verify the message before returning the Publish method
Example Message;
Server Message:
{
ClientId: 13,
msg: "Hello World"
}
Client Response:
{
ClientId: 13,
msg: "{Success: true}"
}
MessageIntercept intercepts all messages, including the request just send which should be ignored due to it beeing a request not a response. However once the client responds with a message would I like to tell the Publish method that the response has been successfully completed and then return true. Else if the client does not respond within 5 seconds it should presume false.
You can use a helper method such as this:
public static async Task<bool> WaitFor(Task task, TimeSpan timeout)
{
return await Task.WhenAny(task, Task.Delay(timeout)) == task;
}
Sample usage:
using System;
using System.Threading.Tasks;
namespace Demo
{
public class Program
{
public static async Task Main()
{
if (await WaitFor(MyAsyncMethod(), TimeSpan.FromSeconds(1)))
Console.WriteLine("First await completed");
else
Console.WriteLine("First await didn't complete");
if (await WaitFor(MyAsyncMethod(), TimeSpan.FromSeconds(3)))
Console.WriteLine("Second await completed");
else
Console.WriteLine("Second await didn't complete");
}
public static async Task MyAsyncMethod()
{
await Task.Delay(2000);
}
public static async Task<bool> WaitFor(Task task, TimeSpan timeout)
{
return await Task.WhenAny(task, Task.Delay(timeout)) == task;
}
}
}
For your Publish() method, the call may look like this:
if (await WaitFor(Publish(1, "msg"), TimeSpan.FromSeconds(5)))
...
However, be aware that the disadvantage of using this approach is that if the timeout is exceeded, then any exceptions thrown by the task will not be observed.
If you need to handle any exceptions that may occur after you've given up waiting for the task, you could pass an exception-handling delegate like so:
public static async Task<bool> WaitFor(Task task, TimeSpan timeout, Action<Exception> handleException)
{
var wrapperTask = task.ContinueWith(
t => handleException(t.Exception.InnerException),
TaskContinuationOptions.OnlyOnFaulted);
return await Task.WhenAny(wrapperTask, Task.Delay(timeout)) == task;
}
Then you could call it like this:
public static async Task Main()
{
if (await WaitFor(
MyAsyncMethod(),
TimeSpan.FromSeconds(1),
exception => Console.WriteLine("Exception: " + exception.Message))
)
Console.WriteLine("First await completed");
else
Console.WriteLine("First await didn't complete");
Console.ReadLine();
}
There is nothing for Publish to wait on, so you need to add a notification hook. An event like "OnMessageIntercept" makes sense to me.
You can then wait on a Task that will be completed by a call to the notification hook.
public async Task<bool> PublishAsync(int clientId, string msg)
{
// Wait and check if the client the message was sent to respond
// if that does not happen within 5 seconds, return false, else true
var messageRecievedSource = new TaskCompletionSource<int>();
void intercept(object sender, MessageInterceptEventArgs args)
{
if (args.ClientId == clientId)
messageRecievedSource.SetResult(clientId);
}
OnMessageIntercept += intercept;
// EDIT
// var completed = Task.WaitAny(Task.Delay(TimeSpan.FromSeconds(5)), messageRecievedSource.Task) > 0;
var completed = await Task.WhenAny(Task.Delay(TimeSpan.FromSeconds(5)), messageRecievedSource.Task);
OnMessageIntercept -= intercept;
// EDIT
// return completed;
return completed == messageRecievedSource.Task;
}
event EventHandler<MessageInterceptEventArgs> OnMessageIntercept;
private async Task MessageIntercept(int clientId, string msg)
{
OnMessageIntercept?.Invoke(this, new MessageInterceptEventArgs(clientId, msg));
// Intercepts all messages
}
class MessageInterceptEventArgs
{
public MessageInterceptEventArgs(int clientId, string msg)
{
ClientId = clientId;
Msg = msg ?? throw new ArgumentNullException(nameof(msg));
}
public int ClientId { get; }
public string Msg { get; }
}
It is an async function, you will have to do "something" in case a result has returned. For example, you could fill in a hidden field value and read it from codebehind again. Or fire another async function and trigger a codebehind method.
To be able to write something after a response, you have to listen to it.
If you want to run an async function synchronously you can use the following code in the System.Threading.Tasks library
Task.Run(async () => await { async method here }).Result
This will cause your asynchronous methods to become blocking, and your code will not continue until you have a response back.
Within your async method you can add a timeout Thread.Sleep(5000) or iterate over the result within the async function (perhaps with a stopwatch if you to avoid Thread.Sleep).
bool Success = await Publish(1,"This is a test")
I have several asynec methods.
One of them triggers a POST method which start a process. I then need to 'sample' the results of another GET method every 10 minutes, and check if the status has changed from "pending" to "success" .
I tryed usingSystem.Threading.Timer with no luck, complaining about my method being asynced .
Error CS0407 'Task Campaigns.repeat(object)' has the wrong return type Campaigns
This is my code:
public async Task waitForCampaignLoadAsync(string Uri)
{
...........
var container = JsonConvert.DeserializeObject<CampaignTempleteStatus>(json);
if(container.status == "pending")
{
var autoEvent = new AutoResetEvent(false);
//The next row triggers the error
var stateTimer = new Timer(repeat, autoEvent, 1000, (1000 * 60 * 10));
//How can I keep repeating this, until (bool isFinished = true)??
}
public async Task repeat(Object stateInfo)
{
if(...)
isFinished = true;
}
Another thing is , how do I pass extra info inside repeat function? I need to pass the Uri input for inner ussage ?
When an asynchronous method starts getting complicated it's a sure sign something is wrong. Most of the time async code looks almost the same as synchronous code with the addition of await.
A simple polling loop could be as simple as :
public async Task<string> waitForCampaignLoadAsync(string uri)
{
var client=new HttpClient();
for(int i=0;i<30;i++)
{
token.ThrowIfCancellationRequested();
var json = await client.GetStringAsync(uri);
var container = JsonConvert.DeserializeObject<CampaignTempleteStatus>(json);
if (container.status != "pending")
{
return container.status;
}
await Task.Delay(10000);
}
return "Timed out!";
}
Cancellation in managed threads explains how CancellationTokenSource and CancellationToken can be used to cancel threads, tasks and asynchronous functions. Many asynchronous methods already provide overloads that accept a CancellationToken parameter. The polling function could be modified to accept and check a canellation token :
public async Task<string> waitForCampaignLoadAsync(string uri,CancellationToken token=default)
{
var client=new HttpClient();
for(int i=0;i<30;i++)
{
var json = await client.GetStringAsync(uri);
var container = JsonConvert.DeserializeObject<CampaignTempleteStatus>(json);
if (container.status != "pending")
{
return container.status;
}
await Task.Delay(10000,token);
}
return "Timed out!";
}
A CancellationTokenSource can be used to call this method with an overall timeout of eg, 5 minutes :
var cts=new CancellationTokenSource(TimeSpan.FromMinutes(5));
try
{
var result=waitForCampaignLoadAsync(uri,cts.Token);
//Process the result ....
}
catch(OperationCancelledExcepction ex)
{
//Handle the timeout here
}
This code can be improved. For example, GetStringAsync() doesn't accept a cancellation token. The operation can be broken in two steps though, one call to GetAsync() with a cancellation token that waits for the server to send a result
and another to HttpContent.ReadAsStringAsync() to read the response, eg :
var response=await client.GetAsync(uri,token)
response.EnsureSuccessStatusCode();
var json=await response.Content.ReadAsStringAsync();
...
The first parameter of Timer is a TimerCallback delegate, which should return void
var stateTimer = new Timer(Repeat, autoEvent, 1000, (1000 * 60 * 10));
private void Repeat(object state)
{
....
}
Please forgive me for any noobish mistakes seen below, I'm learning some of the concepts I'm attempting to work with.
Problem:
While debugging my app, I was able to call an async function with Task.Start(). I felt that the app was in a working state for the phase I'm in so removed all breakpoints with CTRL + SHIFT + F9.
Once I ran the app with no breakpoints it would fail due to a property not getting populated. Now when I try to debug any breakpoint I set in the async function that handles most of the work is longer hit. It's like it is getting skipped. Can anyone see a reason why GetWowAuctionFileInfo isn't being called?
GetWowAuctionFileInfo is what is not getting called, or at least appears to be not getting called.
Thanks.
Relevant Code
Caller Function
private void buttonTestJSFCHI_Click(object sender, RoutedEventArgs e)
{
JSON_Worker w = new JSON_Worker();
w.StartTask("FileInfo", "https://us.api.battle.net/wow/auction/data/medivh?locale=en_US&apikey=<guid>");
foreach (string res in w.ReturnedData)
{
textBoxResults.Text += res;
}
}
Called Functions
public void StartTask(string TaskName, string optionalUri= "no_uri_passed")
{
if (TaskName == "FileInfo")
{
//Need to use a lamba expression to call a delegate with a parameter
if (!(optionalUri == "no_uri_passed"))
{
Task t = new Task(() => GetWowAuctionFileInfo(optionalUri));
t.Start();
//Func<string> function = new Func<string>(() => GetWowAuctionFileInfo(optionalUri));
//Task<string> tInfo = Task<string>.Factory.StartNew(() => GetWowAuctionFileInfo(optionalUri));
}
}
}
private async void GetWowAuctionFileInfo(string auctionInfoUri)
{
RealmJSFileCheck realmInfoObject;
List<string> returnValue = new List<string>();
try
{
using (HttpClient client = new HttpClient())
{
for (int attempt = 0; attempt < 3; attempt++)
{
var response = await client.GetAsync(auctionInfoUri);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
realmInfoObject = JsonConvert.DeserializeObject<RealmJSFileCheck>(content);
returnValue = ConvertFileInfoToConsumableList(realmInfoObject);
//returnValue = realmInfoObject.files.ToString();
break;
}
}
}
}
catch (InvalidOperationException iOpEx)
{
//recieved this when an invalid uri was passed in
}
ReturnedData = returnValue;
}
private List<string> ConvertFileInfoToConsumableList(RealmJSFileCheck jsfc)
{
List<string> returnData = new List<string>();
if (jsfc.files.Count > 0)
{
StringBuilder sb = new StringBuilder();
sb.Append("File URL: ");
sb.Append(jsfc.files[0].url);
returnData.Add(sb.ToString());
sb = new StringBuilder();
sb.AppendLine("Last Modified: ");
sb.Append(jsfc.files[0].lastModified);
returnData.Add(sb.ToString());
}
else
{
returnData.Add("No File Info Found");
}
return returnData;
}
UPDATE
Thanks again all for the detailed commentary. I've gone through much documentation regarding Task usage and learned a lot in this exercise. I'm marking the answer from #Johnathon as the solution because it provided exactly what I was asking for and provided a very helpful link for more information.
Your GetWowAuctionFileInfo method is an asynchronous method, and you await an async call within it without returning a Task. In general it is bad practice to use async void. Instead, turn your GetWowAuctionFileInfo method into async Task<List<string>> GetWowAuctionFileInfo. This will let you await the GetAsync call, parse the data, and return the collection to the caller without having to use a ReturnObject.
private async Task<List<string>> GetWowAuctionFileInfo(string auctionInfoUri)
{
RealmJSFileCheck realmInfoObject;
List<string> returnValue = new List<string>();
try
{
using (HttpClient client = new HttpClient())
{
for (int attempt = 0; attempt < 3; attempt++)
{
var response = await client.GetAsync(auctionInfoUri);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
realmInfoObject = JsonConvert.DeserializeObject<RealmJSFileCheck>(content);
// You can just return the List<T> now.
return ConvertFileInfoToConsumableList(realmInfoObject);
//returnValue = realmInfoObject.files.ToString();
break;
}
}
}
}
catch (InvalidOperationException iOpEx)
{
//recieved this when an invalid uri was passed in
}
}
Because the method was originally async void, you could not await the calling of it in your buttonTestJSFCHI_Click. Now that we've made it all Task based, you can await it within your event handler. Note that event handlers are generally the only acceptable place to use async void. Any time you are responsible for the creation of the methods, and not constrained by a contract (like event handlers), you should always return a Task on your async methods.
private async void buttonTestJSFCHI_Click(object sender, RoutedEventArgs e)
{
JSON_Worker w = new JSON_Worker();
List<string> results = await w.StartTask("FileInfo", "https://us.api.battle.net/wow/auction/data/medivh?locale=en_US&apikey=<guid>");
foreach (string res in results)
{
textBoxResults.Text += res;
}
}
public async Task<List<string>> StartTask(string TaskName, string optionalUri= "no_uri_passed")
{
if (TaskName == "FileInfo")
{
//Need to use a lamba expression to call a delegate with a parameter
if (!(optionalUri == "no_uri_passed"))
{
// Since the GetWowAuctionFileInfo now returns Task, we don't need to create a new one. Just await the Task given back to us, and return the given result.
return await GetWowAuctionFileInfo(optionalUri);
}
}
}
The reason you saw the expected result while debugging is because the debug session was slow enough that the async operation completed in time for your code to use it. When running the app outside of the debugger, it runs faster than the async operation could complete, preventing you from seeing the data. Thus the need to await the entire async call stack, so you can prevent further execution from happening down that code-path until you receive all of the desired data.
Microsoft has a good write up on Task based programming, I'd take a read through it to help you understand it some.
EDIT
Just to clarify, when you return a Task<T> on your methods, you will be given the result when you await. For example:
List<string> result = await StartTask();
Even though StartTask returns Task<List<string>>, the await operation will wait for the StartTask() method to complete, and then unwrap the result from the Task<T> object and give you the result back automatically. So don't let the method signature fool you, if you await it, you will be given back the resulting data, and not the actual Task itself. There won't be any need for you to pull the data out of the Task manually.
Because you not waiting for result.
You loop with ReturnedData before it was assigned with data.
I think you don't need to create new Task at all. Make GetWowAuctionFileInfo method properly asynchronous which returns Task.
private async Task GetWowAuctionFileInfo(string auctionInfoUri)
{
// same code
}
Change StartTask to return Task. Because we not awaiting result here we don't need make method asynchronous.
Suggest to change name of this method to LoadData for example, which give more information about what this method does.
public Task StartTask(string TaskName, string optionalUri= "no_uri_passed")
{
if (TaskName == "FileInfo")
{
//Need to use a lamba expression to call a delegate with a parameter
if (!(optionalUri == "no_uri_passed"))
{
return GetWowAuctionFileInfo(optionalUri) // this will return Task
}
}
// if validation fails - return completed task or throw exception
return Task.CompletedTask;
}
Then you can call it in Button_Click event handler
private async void buttonTestJSFCHI_Click(object sender, RoutedEventArgs e)
{
JSON_Worker w = new JSON_Worker();
await w.StartTask("FileInfo", "yourUrl");
// This line will be executed only after asynchronous methods completes succesfully
// or exception will be thrown
foreach (string res in w.ReturnedData)
{
textBoxResults.Text += res;
}
}
I have an ASP.NET MVC application which needs to check if something exists at 3 remote API servers. The application passes an ID to each API and it returns either true or false. The code looks like this.
public class PingController
{
public async Task<bool> IsFound(int id)
{
var servers = new ['a.com', b.com', 'c.com'];
var result = await foundAtServers(id, servers);
return result;
}
private async Task<bool> foundAtServers(int id, string[] servers)
{
var tasks = from server in servers
select checkServer(id, server);
return await.Task.WhenAll(tasks.ToArray());
}
private async Task<bool> checkServer(id, server)
{
var request = new HttpRequestMessage(HttpMethod.Get, server+"/api/exists"+id);
var client = new HttpClient();
var task = await client.SendAsync(request);
var response = await task.Content.ReadAsStringAsync();
return bool.Parse(response);
}
}
This code currently checks all 3 APIs asynchronously but will wait until ALL of the HttpClient calls have completed before the MVC Action can return.
As soon as one API returns true I want to immediately return true on the Action, rather than wait for the other tasks to complete.
The C# Task class has .WaitAll and .WaitAny, but these won't work either. As I need to cancel the other HttpClient request, I presume I need to use a CancellationToken but I don't know how to use it with this structure.
Cheers.
If you want to immediately return, you can use Task.WhenAny instead of Task.WhenAll. This won't cancel the on-going tasks, but it will enable you to return as soon as possible:
private async Task<bool> FoundAtServersAsync(int id, string[] servers)
{
var tasks = (from server in servers
select checkServer(id, server)).ToList();
while (tasks.Count > 0)
{
var finishedTask = await Task.WhenAny(tasks);
if (finishedTask.Result)
{
return finishedTask.Result;
}
tasks.Remove(finishedTask);
}
return false;
}
This will discard the other tasks. This means that if any exception is thrown inside one of them, it will be swallowed.
Edit:
If you care about actually canceling the other tasks, consider passing your CancellationToken to the overload of SendAsync which takes one, and calling CancellationTokenSource.Cancel once a value is received. Note this will mean you'll also need to handle the OperationCanceledException they will throw.
If they don't matter, i'd simply discard them as above.
This problem is made easier by using the following method to take a sequence of tasks and order them based on when they are completed.
public static IEnumerable<Task<T>> Order<T>(this IEnumerable<Task<T>> tasks)
{
var taskList = tasks.ToList();
var taskSources = new BlockingCollection<TaskCompletionSource<T>>();
var taskSourceList = new List<TaskCompletionSource<T>>(taskList.Count);
foreach (var task in taskList)
{
var newSource = new TaskCompletionSource<T>();
taskSources.Add(newSource);
taskSourceList.Add(newSource);
task.ContinueWith(t =>
{
var source = taskSources.Take();
if (t.IsCanceled)
source.TrySetCanceled();
else if (t.IsFaulted)
source.TrySetException(t.Exception.InnerExceptions);
else if (t.IsCompleted)
source.TrySetResult(t.Result);
}, CancellationToken.None,
TaskContinuationOptions.PreferFairness,
TaskScheduler.Default);
}
return taskSourceList.Select(tcs => tcs.Task);
}
With this you can write:
public static async Task<bool> WhenAny(this IEnumerable<Task<bool>> tasks)
{
foreach (var task in tasks.Order())
if (await task)
return true;
return false;
}
You could wait for the first task to complete - if it's successful, return true immediately. Otherwise, wait for the next one to complete, and so on and so forth.
private async Task<bool> foundAtServers(int id, string[] servers)
{
var tasks = servers.Select(server => checkServer(id, server))
.ToList();
while(tasks.Any())
{
var task = await Task.WhenAny(tasks);
if(task.Result)
return true;
tasks.Remove(task);
}
return false;
}