List from async Task in sync MainPage - c#

I'm really looking for the solution and can't find proper instruction.
I have async method in RestService.cs
public async static Task<List<Convert>> CheckBTCUSDAsync()
{
HttpClient client = new HttpClient();
string restUrl =
"https://bitbay.net/API/Public/BTCUSD/trades.json";
HttpResponseMessage responseGet = await
client.GetAsync(restUrl);
if (responseGet.IsSuccessStatusCode)
{
var response = await responseGet.Content.ReadAsStringAsync();
List<Convert> currencies = Convert.FromJson(response);
//Debug.WriteLine(currencies[0].Date);
return currencies;
}
else
{
//Debug.WriteLine("***************");
//Debug.WriteLine("*****FALSE*****");
//Debug.WriteLine("***************");
return null;
}
}
I want to use it in my MainPage but of course I cant use await in sync method. I found that some devs suggest putting async tasks in eg OnStart method: https://xamarinhelp.com/xamarin-forms-async-task-startup/
I need to to Bind the returned list to picker in Xaml but of course when trying to use:
var convert = RestService.CheckBTCUSDAsync().Result;
It hangs the UI thread. Anyone knows what is the best/easiest way to resolve this?

This is how I got it to work on my app
var convert = Task.Run(() => RestService.CheckBTCUSDAsync()).Result;

Related

How to make a Xamarin.Forms app load its methods on appearing without using Task.Run<T>() in the ctor and block threads [duplicate]

This question already has answers here:
Can constructors be async?
(15 answers)
Closed 25 days ago.
I've read many explanations but none of them made sense to me.
I'm doing this in Xamarin.Forms:
public class SomeClass
{
public SomeClass
{
var request = new GeolocationRequest(GeolocationAccuracy.High, TimeSpan.FromSeconds(10));
var cts = new CancellationTokenSource();
var location = Task.Run<Location>(async () => await Geolocation.GetLocationAsync(request, cts.Token));
var userLat = location.Latitude; // doesn't work
var userLon = location.Longitude; // doesn't work
}
}
The reason I'm doing this is that I'm trying to load all the methods I need such as getting the user's location, show it on the map, load up some pins etc. as soon as the Xamarin.Forms.Maps map appears.
I know it's bad practice from what you guys answered so I'm working on changing that but I'm still having a hard time understanding how to do it differently in the sense of it's confusing. But I'm reading your articles and links to make sure I understand.
I tried to run Task.Run(async () => await) on many methods, and tried to save their values in variables but I can't get the returned value and that's what made me post this question, I need to change my code.
I know could get the returned value using Task.Result() but I've read that this was bad.
How to get the UI to load and wait on the ViewModel to do what it has to do, and then tell the UI to then use whatever the ViewModel is giving him when it is ready ?
You mentioned in a comment to another answer that you can't do it async because this code is in a constructor. In that case, it is recommended to move the asynchronous code into a separate method:
public class MyClass
{
public async Task Init()
{
var request = new GeolocationRequest(GeolocationAccuracy.High, TimeSpan.FromSeconds(10));
var cts = new CancellationTokenSource();
var location = await Geolocation.GetLocationAsync(request, cts.Token);
var userLat = location.Latitude;
var userLon = location.Longitude;
}
}
You can use it the following way:
var myObject = new MyClass();
await myObject.Init();
The other methods of this class could throw an InvalidOperationException if Init() wasn't called yet. You can set a private boolean variable wasInitialized to true at the end of the Init() method. As an alternative, you can make your constructor private an create a static method that creates your object. By this, you can assure that your object is always initialized correctly:
public class MyClass
{
private MyClass() { }
public async Task<MyClass> CreateNewMyClass()
{
var result = new MyClass();
var request = new GeolocationRequest(GeolocationAccuracy.High, TimeSpan.FromSeconds(10));
var cts = new CancellationTokenSource();
var location = await Geolocation.GetLocationAsync(request, cts.Token);
result.userLat = location.Latitude;
result.userLon = location.Longitude;
return result;
}
}
I think it could be this:
public async Task SomeMethodAsync()
{
var request = new GeolocationRequest(GeolocationAccuracy.High, TimeSpan.FromSeconds(10));
var cts = new CancellationTokenSource();
var location = await Geolocation.GetLocationAsync(request, cts.Token);
var userLat = location.Latitude;
var userLon = location.Longitude;
}
Not that methods which contain await calls should be marked as async. If you need to return something from this method then the return type will be Task<TResult>.
Constructors cannot be marked with async keyword. And it's better not to call any async methods from constructor, because it cannot be done the right way with await. As an alternative please consider Factory Method pattern. Check the following article of Stephen Cleary for details: Async OOP 2: Constructors.
The normal way to retrieve results from a Task<T> is to use await. However, since this is a constructor, there are some additional wrinkles.
Think of it this way: asynchronous code make take some time to complete, and you can never be sure how long. This is in conflict with the UI requirements; when Xamarin creates your UI, it needs something to show the user right now.
So, your UI class constructor must complete synchronously. The normal way to handle this is to (immediately and synchronously) create the UI in some kind of "loading..." state and start the asynchronous operation. Then, when the operation completes, update the UI with that data.
I discuss this in more detail in my article on async data binding.
Do the asynchronous work before constructing the object and pass in the data to the constructor. I suggest a factory.
class MyFactory : IMyFactory
{
public async Task<MyClass> GetMyClass()
{
var request = new GeolocationRequest(GeolocationAccuracy.High, TimeSpan.FromSeconds(10));
var cts = new CancellationTokenSource();
var location = Task.Run<Location>(async () => await Geolocation.GetLocationAsync(request, cts.Token));
return new MyClass(location);
}
}
class MyClass
{
public MyClass(Location location)
{
var userLat = location.Latitude;
var userLon = location.Longitude;
//etc....
}
}
To create an instance, instead of
var x = new MyClass();
you'd call
var factory = new MyFactory();
var x = await factory.GetMyClass();
The nice thing about this approach is that you can mock the factory (e.g. for unit tests) in a way that does not depend on the external service.

How to display a System.Threading.Tasks.Task<double> as a label in a WPF app?

I am trying to display the result of my async Task (Below) method as a label in my WPF app.
However when I use the code
Label.Content = Sentiment("en", "text");
and run my application my labels content appears as "System.Threading.Tasks.Task'1[System.Double]"
Also you are not able to convert a Task into any other type as far as I am aware.
So how must I go about getting my labels content to display the Task from my sentiment method?
Thank you.
public async Task<double> Sentiment(string language, string text)
{
HttpClient _httpClient;
_httpClient = new HttpClient();
_httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", "My Secret API Key");
var serviceEndpoint = "https://westus.api.cognitive.microsoft.com/text/analytics/v2.0/sentiment?";
if (string.IsNullOrEmpty(language) || string.IsNullOrEmpty(text))
{
throw new ArgumentNullException();
}
var request = new TextRequest();
request.Documents.Add(new TextDocument(text, language));
var content = new StringContent(JsonConvert.SerializeObject(request), System.Text.Encoding.UTF8, "application/json");
var result = await _httpClient.PostAsync($"{serviceEndpoint}sentiment", content).ConfigureAwait(false);
var response = JObject.Parse(await result.Content.ReadAsStringAsync());
CatchAndThrow(response);
return response["documents"].Children().First().Value<double>("score");
}
So how must I go about getting my labels content to display the Task from my sentiment method?
First, understand that there are two conflicting desires here:
It takes some time to get the data to display from the endpoint.
The computer must have something to display immediately.
The only way to properly satisfy both of these is to immediately display some kind of placeholder value and start the call to the endpoint, and then when the endpoint responds, update the display with the proper data.
Something like this would work in the simple case:
// Immediately, synchronously display placeholder.
Label.Content = "Loading...";
// Asynchronously update with real data.
Label.Content = await Sentiment("en", "text");
I have tried this and now the labels content doesn't change at all.
Please ensure that your code is async all the way, not blocking on async code.
Since you are using WPF, you can also take a more advanced approach that I describe in my article on asynchronous data binding. I have a type NotifyTask<T> which can be used as a data-bindable wrapper for Task<T>. You'd use it as such:
public class MyViewModel
{
public NotifyTask<string> LabelContent { get; }
ViewModel()
{
LabelContent = NotifyTask.Create(
async () => (await Sentiment("en", "text")).ToString(),
"Loading...");
}
}
and then you'd data-bind Label.Content to LabelContent.Result. There are other properties on NotifyTask<T> as well (IsNotCompleted, IsSuccessfullyCompleted, IsFaulted) that allow you to respond to the task's state changes via data binding. E.g., you can show a busy spinner instead of "Loading..." if you want, or show an error if the call to the endpoint fails.
await the result and convert it to string.
var result = await Sentiment("en", "text");
if(result != null)
Label.Content = result.ToString();

C# Ninject: How to bind to method async

I am using Ninject 3.2.2.0 and I am trying to bind an async method.
This is what I got so far:
kernel.Bind<Task<MyInterface>>().ToMethod(async ctx =>
{
var someData = await DoSomethingAsync();
return new SomethingElse(someData);
}).InRequestScope();
My method DoSomethingAsync never gets called. I believe it's not being called because MyInterface is being requested, not Task<MyInterface>.
Any ideas?
The construction of your object graph should be reliable and fast. This excludes doing anything that is I/O related. This means that you should not do anything that is asynchronous at all.
Instead anything that is slow, unreliable or asynchronous should be postponed untill after the object graph is constructed.
I ended up making my binding sync
kernel.Bind<MyInterface>().ToMethod(ctx =>
{
var someData = DoSomethingSync(); // <-- Made this one sync, see method below
return new SomethingElse(someData);
}).InRequestScope();
Here is the 'magic'. I changed my method from:
private async Task<string> DoSomethingAsync() {
var result = await DoSomethingElseAsync();
return result;
}
To
private string DoSomethingSync() {
string result = null;
var runSync = Task.Factory.StartNew(async () => {
result = await DoSomethingElseAsync();
}).Unwrap();
runSync.Wait();
return result;
}
I know performance is affected because of the context switch, but at least it works as expected and you can be sure that it won't deadlock.

how to wait for await in async method to finish the all the executions?

For the following code im calling that async void method.
public void LoadSystemDetails(int clientId)
{
DataSystem.Name = "Salesforce System";
DataSystem.Description = "";
GetAllFields(clientId);
}
following code is GetAllFields method
public async void GetAllFields(int clientId)
{
this.DataSystem.SystemTables = new List<DataSystemTable>();
using (var forceClient = await ConnectToSalesforceByOAuth(clientId))
{
var SalesForceTable = new DataSystemTable
{
TableName = "Contact"
};
DataSystem.SystemTables.Add(SalesForceTable);
var contact = forceClient.DescribeAsync<SalesforceObject>("Contact");
var tableFields = new List<DataSystemField>();
foreach (var con in contact.Result.Fields)
{
tableFields.Add(new DataSystemField
{
ColumnName = con.Name,
});
}
SalesForceTable.EissSyncSystemFields = tableFields;
}
and i call callbackScript as below.
callbackScript.AppendLine(string.Format("var destinationSystem ={0};", JsonConvert.SerializeObject(this.DestinationSystem, Formatting.Indented)));
here DestinationSystem is calling the LoadSystemDetails. Like DestinationSystem.LoadSystemDetails(clientId)
while using (var forceClient = await ConnectToSalesforceByOAuth(clientId)) line is execute at the time the callbackScript is executed. so the SystemTables doesn't have any value. but it having Name and Description.
so here i need to wait the LoadSystemDetails to finish the GetAllFields.
How i do that.Please help me.
Thanks.
If you need LoadSystemDetails to wait for GetAllFields, there are 2 problems here:
you are calling async method from a synchronous context
GetAllFields is async void, which means fire and forget. You will never be able to wait for it to finish.
Solution:
First, NEVER use async void if you need to wait for the result or end. Use async Task instead
Second, either convert LoadSystemDetails to async method also, then await the GetAllFields (that should return Task), or use GetAllFields(clientId).Wait()
take a look at this article for more information on async/await: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
As #netchkin already said: Never use async void. Use async Task. You don't have to return a Task yourself, you can still use return;

Windows phone 8 RestSharp request. Async/await

I know it has been asked a lot, but my problem is, that my method won't wait for the request to be completet, even though i have implemented a TaskCompletionSource, which should have done the job, but it doesn't.
public DecksViewModel(bool local)
{
DList = new List<Deck>();
if (local)
InitializeLocalDeckList();
else
{
Dereffering();
}
}
public async void Dereffering()
{
var e = await InitilaizeWebDeckList();
List<DeckIn> decksIn = JsonConvert.DeserializeObject<List<DeckIn>>(e);
foreach (DeckIn d in decksIn)
{
Deck dadd = new Deck();
dadd.CardCount = 0;
dadd.Name = d.name;
dadd.PicturePath = d.image;
dadd.InstallDirectory = false;
DList.Add(dadd);
}
DataSource = AlphaKeyGroup<Deck>.CreateGroups(DList, System.Threading.Thread.CurrentThread.CurrentUICulture, (Deck s) => { return s.Name; }, true);
}
public Task<String> InitilaizeWebDeckList()
{
var tcs = new TaskCompletionSource<string>();
var client = new RestClient("blabla.com");
var request = new RestRequest("");
request.AddHeader("Authorization", "Basic blabla");
client.ExecuteAsync(request, response =>
{
test = response.Content;
tcs.SetResult(response.Content);
});
return tcs.Task;
}
So when I call the DecksViewModel constructor, I asyncally try to request the data from a webserver and fill the model.
The point is, that the corresponding view "doesn't wait" for the request to fill the model, so it's displayed empty.
I use the
List<AlphaKeyGroup<Deck>> DataSource
to fill a LongListSelector via DataBinding. But DataSource isn't yet set, when it is binded.
I hope you can help
You're calling an async method without awaiting it inside the constructor. That's why "it doesn't wait" (because it has nothing to wait on).
It's usually a bad idea to call an async method inside the constructor for that reason combined with the fact that constructors can't be async.
You should redesign your solution accordingly. An option is to have an async static method that creates an instance and awaits the procedure:
public static async Task CreateInstance(bool local)
{
var model = new DecksViewModel();
if (local)
{
await InitializeLocalDeckList();
}
else
{
await Dereffering();
}
}
That would allow you to not use async void which should only be used in UI even handlers.
You can read more about other options in Stephen Cleary's blog
You are using async void, which means nobody's gonna wait for that. It's just fire and forget.
I see some misunderstanding in the async keyword here:
Your code will only wait for the result of an async method, if you use await. Otherwise that call will just start the async method, but you don't know when it is actually gonna run.
You cannot use await in constructors though.

Categories