I have a main web app which sends the alert to second application and the second application here needs to do more work, meanwhile main application will be waiting for the acknowledgment, hence once the second app receives the request, it should acknowledge by sending a flag and then continue its work.
In order to achieve that i am creating a new method and processing the data there and i am calling that new method using new thread and immediately returning the acknowledgement to main app.
Below is my code, am i following the right approach or is there a better way of doing it ?
public bool Post([FromBody] string content)
{
if (!string.IsNullOrEmpty(content))
{
Thread thread = new Thread(() => AnotherMethod(content));
thread.Start();
}
return true;
}
You could try using async / await
public async Task<bool> Post([FromBody] string content)
{
if (!string.IsNullOrEmpty(content))
{
await AnotherMethod(content);
}
return true;
}
then your AnotherMethod must be async too
Related
So I'm trying to write a simple universal app to get the price of bitcoin from the web. I have an async method that I got from here to get the json from a url and put it into a string. Here is where I called the method:
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
CoinPriceBackend CP = new CoinPriceBackend();
string response = await GetFromAPI();
}
And this is the method:
async Task<string> GetFromAPI()
{
try
{
//Create HttpClient
HttpClient httpClient = new HttpClient();
//Define Http Headers
httpClient.DefaultRequestHeaders.Accept.TryParseAdd("application/json");
//Call
string ResponseString = await httpClient.GetStringAsync(
new Uri("https://api.bitcoinaverage.com/ticker/GBP/"));
//Replace current URL with your URL
return ResponseString;
}
catch (Exception ex)
{
return "ERROR: " + ex;
}
}
I get the error
'The 'await' operator can only be used within an async method.
Consider marking this method with the 'async' modifier and changing its return type to 'Task'.'
But the method is async... How can I fix this?
Thanks!
But the method is async
Take a closer look at the error message; it's not talking about GetFromAPI - it's talking about App.
However, as others have pointed out, constructors cannot be marked async.
I'm trying to write a simple universal app
Universal Windows apps - like all other modern platforms - cannot block the UI thread for I/O-based operations. The user experience is just too bad, and there are tests in most app stores to auto-reject apps that do this.
Put another way: App is called (presumably) on application startup. When the user launches your app, it has to start up quickly and show something ASAP. Waiting for a download to complete is simply not an option.
So, to really fix this, you need to just start the download (not waiting for it to complete) and initialize your application to a "loading" state - showing a spinner or "Loading..." message or whatever. Then, when the download completes, update your app to display what you need to.
I have a blog post on async constructors and an article series on async MVVM (if you're doing MVVM), but a really basic approach could look something like this:
public Task Initialization { get; }
public string Value { get; private set { /* code to raise PropertyChanged */ } }
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
CoinPriceBackend CP = new CoinPriceBackend();
Value = "Loading..."; // Initialize to loading state.
Initialization = InitializeAsync();
}
private async Task InitializeAsync()
{
try
{
string response = await GetFromAPI();
...
Value = response; // Update data-bound value.
}
catch (Exception ex)
{
... // Display to user or something...
}
}
When you use an await within a function you should define the function as async.
But for App() constructor you will be unable to do that. You can defined another function from which you can call your function which return string.
Like this
public App()
{
CallApi();
}
private async void CallApi()
{
response = await GetFromAPI();
}
C# does not allow constructors to be marked as async.
You have three main options:
1) refactor to call this method in async event handler;
2) spawn a new thread using Task.Run and run async code there. This could lead to issues of marshalling result back to UI thread and assigning value to some UI element;
3) make it synchronous (blocking) call. This probably is the easiest option.
You will have to make following changes.
string response = GetFromAPI().Result;
Note that this will probably cause deadlock as Task will try to reaume in the main thread, which is already locked by call to '.Result', therefore you need another change. Also, there is no point
Task<string> GetFromAPI()
{
....
return httpClient.GetStringAsync(new Uri("https://api.bitcoinaverage.com/ticker/GBP/")).ConfigureAwait(false);
...
}
I am trying to invoke a method from another .dll file .
It is sending a message through the VPN then Return the RecievedMessage from another computer.
As you now it takes time to sending and receiving message and VpnObject just send message and I should wait for listener to invoke the RecievedMessage.
This method is like this!
public string RecievedMessage()
{
string Recieved ;
// Some VPN Code and then return the result;
return Recieved;
}
public string SendAndRecieveMessage(string MessageToSend)
{
string RecievedAnswer = string.Empty;
// Now Sending Message through the VPN
VpnObject.SendMessage(MessageToSend);
//Then want to Recieve the answer and return the answer here .
return RecievedAnswer;
}
I'm just thinking how can wait for RecievedMessage to invoke then return the result .
You know it is simple to use a variable and assign it value and check for while but it reduced the performance dramatically .
Is there anyway to continue from SendAndRecieveMessage just when RecievedMessage invoked ? (I think it is something with async and await but don't know how!)
Edit :VpnObject is just a sender and receiver through the vpn . it contains a simple socket send and a listener that invoke a method(RecievedMessage) when new message received .
Whether or not you have an alternative to polling depends on whether the library you are using provides any events or callbacks that will tell you when the request has completed.
Either way, the standard approach to exposing the deferred result of an asynchronous operation is to use a Task. Your method signature would look like this:
public Task<string> SendAndRecieveMessage(string MessageToSend)
Now, how you actually implement the method depends on what API VpnObject exposes. TaskCompletionSource is very useful for this kind of thing.
If VpnObject has an event that fires when the request completes:
public Task<string> SendAndReceiveMessage(string messageToSend)
{
var tcs = new TaskCompletionSource<string>();
...
VpnObject.OnMessageReceived += (s, e) => tcs.SetResult(e.Message);
...
return tcs.Task;
}
If VpnObject can accept a callback that it will invoke when the request completes:
public Task<string> SendAndReceiveMessage(string messageToSend)
{
var tcs = new TaskCompletionSource<string>();
...
VpnObject.OnMessageReceived(message => tcs.SetResult(message));
...
return tcs.Task;
}
If VpnObject doesn't support any of this, you can fall back to polling:
public async Task<string> SendAndReceiveMessage(string messageToSend)
{
var tcs = new TaskCompletionSource<string>();
...
while(!VpnObject.IsMessageReceived)
await Task.Delay(500); // Adjust to a reasonable polling interval
...
return VpnObject.Message;
}
You know it is simple to use a variable and assign it value and check for while but it reduced the performance dramatically .
A spin while loop is definitely not the way to implement this. Even with a sleep, it's clunky, and C# has multiple ways to solve this problem.
It's not entirely clear how your VPN Send and Receive method works, but the idea for solving this is to either use a callback approach, or as you noted, use C# async framework.
Without more details on the VPN Object, I'll just have to have some stub methods. The idea is to create a Task that returns the string, mark it as an async task, then await for it to complete. In your case, the task is receiving the VPN response string.
Something like this.
public Task<string> ReceivedMessage()
{
//get the response from the VPN Object.
string Received = VpnObject.GetResponse();
var ts = new TaskCompletionSource<string>();
ts.SetResult(Received);
// Some VPN Code and then return the result;
return ts.Task;
}
public async Task<string> SendAndReceiveMessageAsync(string MessageToSend)
{
string result = string.Empty;
// Now Sending Message through the VPN
VpnObject.SendMessage(MessageToSend);
result = await ReceivedMessage();
return result;
}
I have a c# windows forms application and use a library which does not provide async-await functionality.
When I press on a button I want to do some work (webrequesting).
While doing this work I dont want to freeze my gui.
I tried several approaches, for example:
public static Task<bool> LoginUser(string username, string password)
{
return Task.Factory.StartNew(() =>
{
try
{
session = new AuthenticatedSession<User>(new User(username), Cryptography.GetMd5(password));
return true;
}
catch (InvalidAuthenticationException)
{
return false;
}
});
}
When I call LoginUser("foo", "bar").Result the gui freezes until the work is done (I understand that this is not async because I can't await new AuthenticatedSession<...
So I look for something like:
Create a thread with a action as parameter
Return the value from the thread
End the thread
Try forcing a new thread (or WorkerThread) instead of using the TaskFactory.
Thread t = new Thread (delegate()
{
try
{
session = new AuthenticatedSession<User>(new User(username), Cryptography.GetMd5(password));
Success(); //coded below
}
catch (InvalidAuthenticationException)
{
Fail();
}
});
t.Start();
Your list requires that we return a value, all we really can do is call a method or set state indicating the return value or even signal (ManualResetEventSlim) if you want some blocking, but your requirements state you want non-blocking.
To resume execution or signal the GUI that your process is done you would invoke some method on the UI thread, like this:
void Success() {
Invoke((MethodInvoker) delegate {
SomeMethodOnTheUI();
});
}
This is basically an async/callback strategy.
So I'm trying to create a proof of concept for server-side asynch using HTTP 202 codes (where the server accepts the task, immediately returns an endpoint to poll and then creates/updates the resource)
Rick Strahl has a description of how to do this in ordinary ASP.NET. That technique depends on being able to Response.End and then continue to execute code. The Response object doesn't even seem to be available in a Web API controller's context.
If the following worked as planned, it would not block returning the http 202 and still guarantee that the database task will run to completion.
//Insert or Update Asych.
public Task<HttpResponseMessage> Post(bool asynch, [FromBody]DatedValue value) //Insert Value
{
Guid key = Guid.NewGuid();
//Want this to run to completion, even if response associated with parent thread is done.
Task toWait = Task.Factory.StartNew(() =>
{
queue.Add(key, 0);
DatedValue justCreated = Insert(value);
queue[key] = justCreated.Id;
});
//Return address to resource just created.
Task<HttpResponseMessage> sender = Task.Factory.StartNew(() =>
{
HttpResponseMessage message = Request.CreateResponse(HttpStatusCode.Accepted);
message.Headers.Location = new Uri("/ValueQueue/" + key);
return message;
});
Task.WaitAll((new[] { toWait, sender }));
return sender;
}
Task.WaitAll blocks execution and response is not returned until both the tasks are completed. If you change your code something like below, you should be able to return the response while the task is being run.
public HttpResponseMessage Post(bool asynch, [FromBody]DatedValue value)
{
Guid key = Guid.NewGuid();
Task.Factory.StartNew(() =>
{
queue.Add(key, 0);
DatedValue justCreated = Insert(value);
queue[key] = justCreated.Id;
});
HttpResponseMessage message = Request.CreateResponse(HttpStatusCode.Accepted);
message.Headers.Location = new Uri("/ValueQueue/" + key);
return message;
}
However, you should be aware of a problem with an approach like this. If you are hosting Web API on IIS, your worker process can get recycled while your task is running. Since you have already returned the response, as far as ASP.NET is concerned, it has completed its work. So, if for some reason IIS decides to recycle the worker process, it will go ahead regardless of where your task is in terms of execution and hence you can end up with corrupt data.
With .NET 4.5.2, you can use QueueBackgroundWorkItem. Read this - http://blogs.msdn.com/b/webdev/archive/2014/06/04/queuebackgroundworkitem-to-reliably-schedule-and-run-long-background-process-in-asp-net.aspx.
I would like to answer a request, but continue processing code.
I tried something like:
[HttpPost]
public async Task<HttpResponseMessage> SendAsync(MyRequest sms)
{
await Task.Run(() => Process(sms)); //need to run in a separate thread
var response = new MyRequest(sms) { Ack = true };
return Request.CreateResponse(HttpStatusCode.Created, response.ToString());
}
private async void Process(MyRequest sms)
{
var validationResult = new MyRequestValidation(_p2pContext, _carrierService).Validate(sms);
if (string.IsNullOrWhiteSpace(validationResult.Errors[0].PropertyName)) // Request not valid
return;
Message msg;
if (validationResult.IsValid)
{
msg = await _messageService.ProcessAsync(sms);
}
else // Create message as finished
{
msg = _messageService.MessageFromMyRequest(sms,
finished: true,
withEventSource: validationResult.Errors[0].CustomState.ToString()
);
}
// Salve in db
_p2pContext.MessageRepository.Create(msg);
_p2pContext.Save();
}
I would like to answer a request, but continue processing code.
Are you sure you want to do this in ASP.NET? This is not a situation that ASP.NET (or any web server) was designed to handle.
The classic (correct) way to do this is to queue the work to a persistent queue and have a separate backend process do the actual processing of that work.
There are all kinds of dangers with doing processing inside of ASP.NET outside of a request context. In general, you can't assume that the work will ever actually be done. If you're OK with that (or just like to live dangerously), then you can use HostingEnvironment.QueueBackgroundWorkItem.
I have a blog post that goes into more detail.