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.
Related
I have an interface that is defined as follows:
internal interface IHttpService
{
Task SendGetRequest(string param);
}
And the following concrete class (obviously there is compilation errors):
public class HttpService : IHttpService
{
private readonly HttpClient client;
private const string httpLink = "https://somesite.org/search?q=";
private const string httpSuffix = "&format=json&ads=1";
public HttpService()
{
client = new HttpClient();
client.DefaultRequestHeaders.Add("user-agent", "myapp");
}
public async Task SendGetRequest(string param)
{
var response = await client.GetAsync(httpLink + param + httpSuffix);
return response.Content.ReadAsStringAsync();
}
}
So I obviously get a compilation error when returning the ReadAsStringAsync function, but I want my viewmodel to get the response from this function. My viewmodel is as follows:
public SearchViewModel()
{
httpService = (App.Current as App).Container.GetService<IHttpService>();
SearchCommand = new RelayCommand(() =>
{
// Will need to do some proper validation here at some point
var response = await httpService.SendGetRequest(httpStringToSend);
});
}
I'm sure i'm missing something but i'm not entirely sure what...
ReadAsStringAsync is asynchronous and needs to be awaited.
You also need to use the generic Task<T> as your return type rather than Task, because your asynchronous operation is returning a value i.e. string.
public async Task<string> SendGetRequest(string param)
{
var response = await client.GetAsync(httpLink + param + httpSuffix);
return await response.Content.ReadAsStringAsync();
}
HttpResponseMessage is also IDisposable so you should add a using block:
public async Task<string> SendGetRequest(string param)
{
using (var response = await client.GetAsync(httpLink + param + httpSuffix))
{
return await response.Content.ReadAsStringAsync();
}
}
I have the following code section in which SavePersonAsync method were calling 3 external methods and could anyone tell me the implementation of async/await methods were implemented correctly. Does any of the call chains in which async/await was not implemented correctly?
public PersonService(IPersonDbService personDbService,
IPersonEntityToPersonModelMapper personEntityToPersonModelMapper,
IPersonModelToPersonEntityMapper personModelToPersonEntityMapper)
{
_personDbService = personDbService;
_personEntityToPersonModelMapper = personEntityToPersonModelMapper;
_personModelToPersonEntityMapper = personModelToPersonEntityMapper;
}
public async Task<IEnumerable<PersonModel>> SavePersonAsync(IEnumerable<PersonModel> personsModel)
{
var personsEntity = await _personModelToPersonEntityMapper.MapPersonModelToPersonEntityAsync(personsModel);
var savedPersons = await _personDbService.SavePersonAsync(personsEntity.First()); // Currently only alow one person at a time, later it will allow to save a collection of persons
return await _personEntityToPersonModelMapper.MapPersonEntityToPersonModelyAsync(new List<PersonEntity>
{
savedPersons
});
}
}
public class PersonEntityToPersonModelMapper : IPersonEntityToPersonModelMapper
{
public async Task<IEnumerable<PersonModel>> MapPersonEntityToPersonModelyAsync(
IEnumerable<PersonEntity> personsEntity)
{
if (personsEntity.NullOrEmpty())
return await Task.FromResult(Enumerable.Empty<PersonModel>());
return await Task.FromResult(personsEntity.Select(personEntity => new PersonModel
{
FirstName = personEntity.FirstName,
LastName = personEntity.LastName
}));
}
}
public class PersonModelToPersonEntityMapper : IPersonModelToPersonEntityMapper
{
public async Task<IEnumerable<PersonEntity>> MapPersonModelToPersonEntityAsync(IEnumerable<PersonModel> personsModel)
{
if (personsModel.NullOrEmpty())
return await Task.FromResult(Enumerable.Empty<PersonEntity>());
return await Task.FromResult(personsModel.Select(personModel => new PersonEntity
{
FirstName = personModel.FirstName,
LastName = personModel.LastName
}));
}
}
From the definition of async await when you have written await the flow will start sequential, As you need the output to be used in the next statement then there is not much use of async await in your case
To breief about how it works
Suppose you have to method call which can work async in that can your code would be like:
Var a = GetMethodA();
Var b = GetMethodB();
await a;
await b;
In this case your method will work asynchronous
I am trying to write a function which uses Task and TaskCompletion.
My problem is that after login, the result is not returned. I used similar code before and it was working. I do not know what causes for this situation.
public async Task<byte[]> Sign(byte[] documentContent)
{
var service = new SignServiceWrapper("https://example.com?wsdl");
var loginResult = await Task.Run(() => service.Login(loginRequest));
//....
}
and my SignServiceWrapper class
public class SignServiceWrapper
{
private static string _webServiceUrl;
private BrokerClientClient client;
public SignServiceWrapper(string webServiceUrl)
{
_webServiceUrl = webServiceUrl;
}
public Task<loginResponse> Login(loginRequest request)
{
var tcs = new TaskCompletionSource<loginResponse>();
ClientGenerator.WebServiceUrl = _webServiceUrl;
ClientGenerator.InitializeService();
client = ClientGenerator.ServiceClient;
client.loginCompleted += (sender, loginResult) =>
{
if (loginResult.Error != null)
tcs.SetException(loginResult.Error);
else
tcs.TrySetResult(loginResult.Result);
};
client.loginAsync(request);
return tcs.Task;
}
// ...
}
If I call my login function like that it works
var loginResult = Task.Run(() => service.Login(loginRequest));
loginResult.Wait();
I know that there is kind of a deadlock but I don't know how to solve this here and which object.
Here is a working .NET Fiddle.
I think your .Login method is trying to do too much. The first thing that I noticed (and can only imagine how it's implemented) is the static ClientGenerator, that has static mutable state. This which is alarming and a very specific code smell. I would love to see what the client itself looks like and how that is implemented as that would certainly help to better answer this question.
Based on what you shared thus far (and assuming that the client.loginAsync returns a Task<loginResponse>), I would say that you could do the following:
public class SignServiceWrapper
{
private static string _webServiceUrl;
private BrokerClientClient client;
public SignServiceWrapper(string webServiceUrl)
{
_webServiceUrl = webServiceUrl;
}
public Task<loginResponse> LoginAsync(loginRequest request)
{
ClientGenerator.WebServiceUrl = _webServiceUrl;
ClientGenerator.InitializeService();
client = ClientGenerator.ServiceClient;
return client.loginAsync(request);
}
// ...
}
You could then consume this as such:
public async Task<byte[]> Sign(byte[] documentContent)
{
var service = new SignServiceWrapper("https://example.com?wsdl");
var loginResult = await service.LoginAsync(loginRequest);
//...
}
If the client.loginAsync doesn't return what you're looking for, then you'll need to approach this doing something similar to your current approach. Or if you are locked in to the event-based async pattern, you have other considerations - like whether or not you want to support cancellation, IsBusy, progress, incremental results and if you have the ability to have the event args inherit the System.ComponentModel.AsyncCompletedEventArgs, etc...
One final consideration, if the client.loginAsync is Task returning, even if it doesn't return the loginResponse you need to await it like so:
public async Task<loginResponse> Login(loginRequest request)
{
var tcs = new TaskCompletionSource<loginResponse>();
ClientGenerator.WebServiceUrl = _webServiceUrl;
ClientGenerator.InitializeService();
client = ClientGenerator.ServiceClient;
client.loginCompleted += (sender, loginResult) =>
{
if (loginResult.Error != null)
tcs.SetException(loginResult.Error);
else
tcs.TrySetResult(loginResult.Result);
};
await client.loginAsync(request);
return tcs.Task;
}
Update
After discussion with OP this .NET Fiddle seemed to align with his needs.
Change var loginResult = await Task.Run(() =>service.Login(loginRequest));
To var loginResult = await service.Login(loginRequest);
I have created a function that returns an object using async/await. I would like to make the function generic so that it can return whatever object that I pass in. The code is boilerplate except for the objects being returned. I would like to be able to call GetAsync and have it return the correct object
public Patron getPatronById(string barcode)
{
string uri = "patrons/find?barcode=" + barcode;
Patron Patron = GetAsync(uri).Result;
return Patron;
}
private async Task<Patron> GetAsync(string uri)
{
var client = GetHttpClient(uri);
var content = await client.GetStringAsync(uri);
JavaScriptSerializer ser = new JavaScriptSerializer();
Patron Patron = ser.Deserialize<Patron>(content);
return Patron;
}
What about a generic method?
private async Task<T> GetAsync<T>(string uri)
{
var client = GetHttpClient(uri);
var content = await client.GetStringAsync(uri);
var serializer = new JavaScriptSerializer();
var t = serializer.Deserialize<T>(content);
return t;
}
Normally, you should place this method into another class and make it public, in order it can be used by methods in different classes.
Regarding the way you call this method, you could try the following:
// I capitalized the first letter of the method,
// since this is a very common convention in .NET
public Patron GetPatronById(string barcode)
{
string uri = "patrons/find?barcode=" + barcode;
var Patron = GetAsync<Patron>(uri).Result;
return Patron;
}
Note: In the above snippet I assumed that you haven't moved the GetAsync into another class. If you move it, then you have to make a slight change.
Update
I'm not following what you mean by your note. Do I need to make GetPatronById a task function as well - like Yuval has done below?
I mean something like this:
// The name of the class may be not the most suitable in this case.
public class Repo
{
public static async Task<T> GetAsync<T>(string uri)
{
var client = GetHttpClient(uri);
var content = await client.GetStringAsync(uri);
var serializer = new JavaScriptSerializer();
var t = serializer.Deserialize<T>(content);
return t;
}
}
public Patron GetPatronById(string barcode)
{
string uri = "patrons/find?barcode=" + barcode;
var Patron = Repo.GetAsync<Patron>(uri).Result;
return Patron;
}
Generic can be easily done with:
private async Task<T> GetAsync(string uri)
{
var client = GetHttpClient(uri);
var content = await client.GetStringAsync(uri);
return JsonConvert.DeserializeObject<T>(content);
}
Things to note:
JavaScriptSerializer has been deprecated for ages, avoid using it. Try out Json.NET instead.
This:
Patron Patron = GetAsync(uri).Result;
is dangerous and can cause potential deadlocks, especially in Web API. You need to go "async all the way":
public Task<Patron> GetPatronByIdAsync(string barcode)
{
string uri = $"patrons/find?barcode={barcode}";
return GetAsync<Patron>(uri);
}
And only your top most level invoker need await on the Task. Possibly some controller action:
public async Task SomeAction()
{
await GetPatronByIdAsync("hello");
}
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)