Async call inside synchronous webservice - c#

I have a web service which calls an external web service. The external service responds after 3-4 seconds.
Right now all the calls are synchronous, but does it make sense to use async calls instead (sample below)?
Will it help with performance (not keeping threads blocked)? Isn't the thread blocked on the first line of GetData()?
Thank you.
public class MyService : WebService
{
[WebMethod]
public string GetData()
{
string response = ExecuteRequest(externalUrl, someContent).Result;
return response;
}
private async Task<string> ExecuteRequest(string url, string content)
{
var httpResponse = await new HttpClient().PostAsync(url, new StringContent(content));
string responseStr = await httpResponse.Content.ReadAsStringAsync();
return responseStr;
}
}

To answer your question: Yes, it does make sense to use async calls instead, but your example is not async. If you wanted to make it async you'd have to do something like this:
public class MyService : WebService
{
[WebMethod]
public async Task<string> GetData()
{
string response = await ExecuteRequest(externalUrl, someContent);
return response;
}
private async Task<string> ExecuteRequest(string url, string content)
{
var httpResponse = await new HttpClient().PostAsync(url, new StringContent(content));
string responseStr = await httpResponse.Content.ReadAsStringAsync();
return responseStr;
}
}

Related

How to call API in Discord.NET command?

I have problem with calling API in my Discord Bot, I am trying to learn async, await but it seems as I am stuck.
This is my Call API class where I am calling API.
public async Task<Root> GetInfoAsync()
{
string path = "https://onemocneni-aktualne.mzcr.cz/api/v2/covid-19/zakladni-prehled.json";
Root data = null;
HttpResponseMessage response = await client.GetAsync(path);
if (response.IsSuccessStatusCode)
{
string json = await response.Content.ReadAsStringAsync();
data = JsonConvert.DeserializeObject<Root>(json);
}
return data;
}
public async Task<string> VypisAsync()
{
Root data = await this.GetInfoAsync();
int deaths = data.data[0].umrti,
recovered = data.data[0].vyleceni,
positive = data.data[0].aktivni_pripady,
hospitalized = data.data[0].aktualne_hospitalizovani;
return $"Aktualni situace v ČR:\n" +
$"vyleceni: {recovered}\n" +
$"aktualne nemocni: {positive}\n" +
$"hospitalizovani: {hospitalized}\n" +
$"zemreli: {deaths}";
}
And here is my covid command
public class CovidModule : ModuleBase<SocketCommandContext>
{
// ~void -> current covid situation in CZ
[Command("covid")]
[Summary("shows current covid situation in CZ")]
public async Task<Task> CovidAsync()
{
CovidApi api = new CovidApi();
return ReplyAsync(await api.VypisAsync());
}
}
I know that all others commands return Task, but I don't know how to make it like that.
ReplyAsync() is an async method, so you need to await it.
Your CovidAsync() method isn't returning an actual value, so in the synchronous world its return value would be void. Since it's an async method you return Task instead.
public async Task CovidAsync()
{
CovidApi api = new CovidApi();
await ReplyAsync(await api.VypisAsync());
}
As an aside, it would be better to have CovidApi as a member of your CovidModule. That way you don't need to keep newing it up in each method.

Multiple operations with path 'api/External' and method 'GET' while calling an external Web API

I am trying to call an externally hosted Web API within my Web API Application.
While attempting to use Get () method, everything seems to work fine.
However, when I try to implement Get(int value) I am getting an error:
Multiple operations with path 'api/External' and method 'GET'.
What is wrong with my Controller?
public class ExternalController : ApiController
{
static string _address = "http://localhost:00000/api/Values";
private string result;
// GET api/values
public async Task<IEnumerable<string>> Get()
{
var result = await GetExternalResponse();
return new string[] { result, "value2" };
}
private async Task<string> GetExternalResponse()
{
var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(_address);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
return result;
}
public async Task<string> Get(int value)
{
var result = await GetExternalResponse();
return result;
}
}
I have also tried the below approach, which also seems to throw the same error:
private async Task<string> GetExternalResponse2(int value)
{
var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(_address + "/" + value);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
return result;
}
public async Task<string> Get(int value)
{
var result = await GetExternalResponse2(value);
return result;
}
When you do your get like this
Get api/External/{id}
web api is not sure whether to call the Get Method with no parameter or the Get Method with Parameter because of the default routing defined in your web api config
I would suggest using attribute routing to fix your problem
[Route("api/External/Get")]
public async Task<IEnumerable<string>> Get()
[Route("api/External/Get/{id}")]
public async Task<string> Get(int value)

WebClient GET/POST without blocking UI

I would like to make non blocking GET and POST requests. I've managed to solve it with BackgroundWorker, but I need your help to achieve it using tasks.
public Task<string> Post(Uri uri, string data)
{
return _webClient.UploadStringTaskAsync(uri, data);
}
public Task<string> Get(Uri uri)
{
return _webClient.DownloadStringTaskAsync(uri);
}
I need the requests to run sequentially. What's the proper way to implement this? Flag methods with async and await them? Wait for each task using Task.WaitAll()?
An example of what I'm after:
Task<string> loginTask = Post("login", data);
// wait for webrequest to complete and make use of response string
// use data from first request in a new request:
Task<string> someOtherRequest = Get("details");
You can use await Task.Run():
await Task.Run(()=> { Run_UI_Blocking_Function(); });
For example:
In the Post() function, you can add this:
await Task.Run(()=> { PostString = Post(new Uri("url"), "data"); });
And in the 'Get()' function, you can add this:
await Task.Run(()=> { GetString = Get(new Uri("url")); });
You'd just need to add async to your calling function. Here's a completed example:
private async void btn_PostData_Click(object sender, EventArgs e)
{
await Task.Run(()=> { PostString = Post(new Uri("url"), "data"); });
}
Hope this helps. It's really short and sweet.
You need to mark the method as async, and await the result.
public async Task<string> Get(Uri uri)
{
return await _webClient.DownloadStringTaskAsync(uri);
}
You can also install the Microsoft HttpClient library, which is already plumbed for async calls and may be a little lighter weight.

Async await not returning at all(.Net 4.5)

I have the sample code below in an ASP.NET MVC application.
I am not able to get the data at all. In otherwords even though I am awaiting for the completion of the task Nothing happens and control never returns to the Index Action.
What am I missing?
public class HomeController : Controller
{
//
// GET: /Home/
public async Task<ActionResult> Index()
{
Task<string> getContentsTask = GetContentsAsync();
string contents = await getContentsTask;
return Content("ssss");
}
async Task<string> GetContentsAsync()
{
int sample = 0;
HttpClient client = new HttpClient();
Task<string> contents = client.GetStringAsync("http://www.msdn.com");
string data = await contents;
return data;
}
}
Tried this in a console application and even in this the application exited as soon as i called the first await.
class Program
{
static void Main(string[] args)
{
GetContents();
}
public static async void GetContents()
{
Task<string> newTask = GetContentsAsync();
string contents = await newTask;
Console.WriteLine(contents);
Console.ReadLine();
}
public static async Task<string> GetContentsAsync()
{
HttpClient client = new HttpClient();
Task<string> getStringTask = client.GetStringAsync("http://www.msdn.com");
string content = await getStringTask;
return content;
}
}
When I give a Thread.Sleep(8000); then this is working fine. The calls are returning correctly.
Task<string> getContentsTask = GetContentsAsync();
Thread.Sleep(10000);
string contents = await getContentsTask;
Please ensure that you are using .NET 4.5 and that you have httpRuntime.targetFramework set to 4.5 in your app.config.

async method callback with Task.ContinueWIth?

I have a method that pulls some HTML via the HttpClient like so:
public static HttpClient web = new HttpClient();
public static async Task<string> GetHTMLDataAsync(string url)
{
string responseBodyAsText = "";
try
{
HttpResponseMessage response = await web.GetAsync(url);
response.EnsureSuccessStatusCode();
responseBodyAsText = await response.Content.ReadAsStringAsync();
}
catch (Exception e)
{
// Error handling
}
return responseBodyAsText;
}
I have another method that looks like so:
private void HtmlReadComplete(string data)
{
// do something with the data
}
I would like to be able to call GetHTMLDataAsync and then have it call HtmlReadComplete on the UI thread when the html has been read. I naively thought this could somehow be done with something that looks like
GetHTMLDataAsync(url).ContinueWith(HtmlReadComplete);
But, I can't get the syntax correct, nor am I even sure that's the appropriate way to handle it.
Thanks in advance!
public async void ProcessHTMLData(string url)
{
string HTMLData = await GetHTMLDataAsync(url);
HTMLReadComplete(HTMLData);
}
or even
public async void ProcessHTMLData(string url)
{
HTMLReadComplete(await GetHTMLDataAsync(url));
}
You're close, but ContinueWith() takes a delegate with Task as its parameter, so you can do:
GetHTMLDataAsync(url).ContinueWith(t => HtmlReadComplete(t.Result));
Normally, you should be careful with using Result together with async, because Result blocks if the Task hasn't finished yet. But in this case, you know for sure that the Task is complete, you Result won't block.

Categories