I am writing application on WP 8.1. One of my method is parsing html and everything was ok. But I want to change coding to have polish characters.
So I must to have Length properties to variable type byte[]. To make this possible I need to use await and changed my method on asnych.
public async void GetTimeTable(string href, int day)
{
string htmlPage = string.Empty;
using (var client = new HttpClient())
{
var response = await client.GetByteArrayAsync(URL);
char[] decoded = new char[response.Length];
for (int i = 0; i < response.Length; i++)
{
if (response[i] < 128)
decoded[i] = (char)response[i];
else if (response[i] < 0xA0)
decoded[i] = '\0';
else
decoded[i] = (char)iso8859_2[response[i] - 0xA0];
}
htmlPage = new string(decoded);
}
// further code... and on the end::
TimeTableCollection.Add(xxx);
}
public ObservableCollection<Groups> TimeTableCollection { get; set; }
Method is calling from MainPage.xaml.cs
vm.GetTimeTable(navContext.HrefValue, pivot.SelectedIndex);
TimeTableViewOnPage.DataContext = vm.TimeTableCollection;
And now is my question. Why vm.TimeTableCollection is null? When I don't use async and await everything is ok and vm.TimeTableCollection has x elements.
And now is my question. Why vm.TimeTableCollection is null?
Because you're executing an async operation without awaiting it. Hence, the request may not be complete when you access your vm property in the next line.
You need to change your method signature to async Task and await it:
public async Task GetTimeTableAsync(string href, int day)
{
string htmlPage = string.Empty;
using (var client = new HttpClient())
{
var response = await client.GetByteArrayAsync(URL);
char[] decoded = new char[response.Length];
for (int i = 0; i < response.Length; i++)
{
if (response[i] < 128)
decoded[i] = (char)response[i];
else if (response[i] < 0xA0)
decoded[i] = '\0';
else
decoded[i] = (char)iso8859_2[response[i] - 0xA0];
}
htmlPage = new string(decoded);
}
// further code... and on the end::
TimeTableCollection.Add(xxx);
}
and then:
await vm.GetTimeTableAsync(navContext.HrefValue, pivot.SelectedIndex);
This means your top calling method has to become async as well. This is usually the behavior when dealing with async methods, you need to go async all the way.
Note, to follow TPL guidelines, you should mark any async method with the Async postfix, Hence GetTimeTable should be GetTimeTableAsync
You're not awaiting the result:
await vm.GetTimeTable(navContext.HrefValue, pivot.SelectedIndex);
TimeTableViewOnPage.DataContext = vm.TimeTableCollection;
If you don't await an async method, the program will execute it, and continue to execute the following code without waiting for it to finish.
Related
I have some code that looks like this:
async void MyMethod()
{
var client = new HttpClient();
var tasks = new List<Task<string>>();
for (int id = 1; id <= 10; id++)
{
tasks.Add(GetValue(client, id));
}
await Task.WhenAll(tasks);
}
async Task<string> GetValue(HttpClient client, int id)
{
var response = await client.GetAsync($"http://example.com?id={id}");
// Do something else here so that this task can't be condensed to a
// single line of code
System.Diagnostics.Debug.WriteLine("something");
return await response.Content.ReadAsStringAsync();
}
What is the syntax to add anonymous tasks to tasks without the use of the GetValue method? I want to do something like the code below, but I can't figure out the syntax to return a Task<string> from my anonymous method (this code gives me a compilation error Cannot convert async lambda expression to delegate type Func<string>):
for (int id = 1; id <= 10; id++)
{
tasks.Add(new Task<string>(async () =>
{
var response = await client.GetAsync($"http://example.com?id={id}");
// Do something else here so that this task can't be condensed to a
// single line of code
System.Diagnostics.Debug.WriteLine("something");
return await response.Content.ReadAsStringAsync();
}));
}
The way you execute a delegate is pretty simple, you just invoke it.
public static T Execute<T>(Func<T> func)
{
return func();
}
for (int id = 1; id <= 10; id++)
{
tasks.Add(Execute(async () =>
{
var response = await client.GetAsync($"http://example.com?id={id}");
return await response.Content.ReadAsStringAsync();
}));
}
I'm using ASP.NET Core MVC 2.0 and I've got an API that calls a method which opens a DB connection and inserts values into a model.
I'm hitting the same method 7 times and I'm just passing different information into it the parameters to build out my model. I HAVE to hit it seven different times, no rewrite of code will prevent this.
Instead of creating a loop and calling that method I wish to make parallel calls to the db so the response is faster. I've found multiple articles explaining how to do this e.g. How to properly make asynchronous / parallel database calls but there's enough difference that I can't seem to get it to work.
Following is the my method hitting the DB:
public async Task<RS_Model> Get_Results(RequestS_Model values)
{
//Deleted a bunch of code to keep it simple
string strQS = #"EXEC Get param1,param2,etc";
using (SqlConnection conSQL = new SqlConnection(connectionString))
{
using (SqlCommand cmdSQL = new SqlCommand(strQS, conSQL))
{
conSQL.Open();
using (SqlDataReader dtrSQL = cmdSQL.ExecuteReader())
{
while (dtrSQL.Read())
{
Double.TryParse(dtrSQL["Value1"].ToString(), out dblVal1);
} //Ends While
} //end SQLDataReader
} //Ends cmdSQL
} //ends using
results.Price = dblVal1;
return results;
} //Ends Get Results
My IActionResult for the api is:
[HttpGet]
public async Task<IActionResult> Get([FromQuery] RequestS_Model values)
{
SV_Results Results = new SV_Results();
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
RS_Model model = new RS_Model();
model.dblr[0] = await Results.Get_Results(values);
values.Parm1 = 2;
model.dblr[1] = await Results.Get_Results(values);
values.Parm1 = 3;
model.dblr[2] = await Results.Get_Results(values);
values.Parm1 = 4;
model.dblr[3] = await Results.Get_Results(values);
values.Parm1 = 5;
model.dblr[4] = await Results.Get_Results(values);
values.Parm1 = 6;
model.dblr[5] = await Results.Get_Results(values);
values.Parm1 = 7;
model.dblr[6] = await Results.Get_Results(values);
//int[] results = await Task.WhenAll(new Task<int>[] { task1, task2 });
return new OkObjectResult(model);
} //IActionResults
I know that I've forced them into a synchronous call be doing what I am, but I can't seem to make the 7 calls asynchronous and then wait for them to be all done before I build my final model. The final response needs to be in Json, but I haven't even gotten that far yet.
Instead of this:
model.dblr[0] = await Results.Get_Results(values);
model.dblr[1] = await Results.Get_Results(values);
model.dblr[2] = await Results.Get_Results(values);
model.dblr[3] = await Results.Get_Results(values);
model.dblr[4] = await Results.Get_Results(values);
model.dblr[5] = await Results.Get_Results(values);
model.dblr[6] = await Results.Get_Results(values);
Create a list of tasks and await them as a group:
var tasks = Enumerable.Range(0,7).Select( i => Results.Get_Results(values) ).ToList();
await Task.WhenAll(tasks);
for (int i=0; i<7; i++) model.dblr[i] = tasks[i].Result;
How to get the individual API call status success response in C#.
I am creating a mobile application using Xamarin Forms,
In my application, I need to prefetch certain information when app launches to use the mobile application.
Right now, I am calling the details like this,
public async Task<Response> GetAllVasInformationAsync()
{
var userDetails = GetUserDetailsAsync();
var getWageInfo = GetUserWageInfoAsync();
var getSalaryInfo = GetSalaryInfoAsync();
await Task.WhenAll(userDetails,
getWageInfo,
getSalaryInfo,
);
var resultToReturn = new Response
{
IsuserDetailsSucceeded = userDetails.Result,
IsgetWageInfoSucceeded = getWageInfo.Result,
IsgetSalaryInfoSucceeded = getSalaryInfo.Result,
};
return resultToReturn;
}
In my app I need to update details based on the success response. Something like this (2/5) completed. And the text should be updated whenever we get a new response.
What is the best way to implement this feature? Is it possible to use along with Task.WhenAll. Because I am trying to wrap everything in one method call.
In my app I need to update details based on the success response.
The proper way to do this is IProgress<string>. The calling code should supply a Progress<string> that updates the UI accordingly.
public async Task<Response> GetAllVasInformationAsync(IProgress<string> progress)
{
var userDetails = UpdateWhenComplete(GetUserDetailsAsync(), "user details");
var getWageInfo = UpdateWhenComplete(GetUserWageInfoAsync(), "wage information");
var getSalaryInfo = UpdateWhenComplete(GetSalaryInfoAsync(), "salary information");
await Task.WhenAll(userDetails, getWageInfo, getSalaryInfo);
return new Response
{
IsuserDetailsSucceeded = await userDetails,
IsgetWageInfoSucceeded = await getWageInfo,
IsgetSalaryInfoSucceeded = await getSalaryInfo,
};
async Task<T> UpdateWhenComplete<T>(Task<T> task, string taskName)
{
try { return await task; }
finally { progress?.Report($"Completed {taskName}"); }
}
}
If you also need a count, you can either use IProgress<(int, string)> or change how the report progress string is built to include the count.
So here's what I would do in C# 8 and .NET Standard 2.1:
First, I create the method which will produce the async enumerable:
static async IAsyncEnumerable<bool> TasksToPerform() {
Task[] tasks = new Task[3] { userDetails, getWageInfo, getSalaryInfo };
for (i = 0; i < tasks.Length; i++) {
await tasks[i];
yield return true;
}
}
So now you need to await foreach on this task enumerable. Every time you get a return, you know that a task has been finished.
int numberOfFinishedTasks = 0;
await foreach (var b in TasksToPerform()) {
numberOfFinishedTasks++;
//Update UI here to reflect the finished task number
}
No need to over-complicate this. This code will show how many of your tasks had exceptions. Your await task.whenall just triggers them and waits for them to finish. So after that you can do whatever you want with the tasks :)
var task = Task.Delay(300);
var tasks = new List<Task> { task };
var faultedTasks = 0;
tasks.ForEach(t =>
{
t.ContinueWith(t2 =>
{
//do something with a field / property holding ViewModel state
//that your view is listening to
});
});
await Task.WhenAll(tasks);
//use this to respond with a finished count
tasks.ForEach(_ => { if (_.IsFaulted) faultedTasks++; });
Console.WriteLine($"{tasks.Count() - faultedTasks} / {tasks.Count()} completed.");
.WhenAll() will allow you to determine if /any/ of the tasks failed, they you just count the tasks that have failed.
public async Task<Response> GetAllVasInformationAsync()
{
var userDetails = GetUserDetailsAsync();
var getWageInfo = GetUserWageInfoAsync();
var getSalaryInfo = GetSalaryInfoAsync();
await Task.WhenAll(userDetails, getWaitInfo, getSalaryInfo)
.ContinueWith((task) =>
{
if(task.IsFaulted)
{
int failedCount = 0;
if(userDetails.IsFaulted) failedCount++;
if(getWaitInfo.IsFaulted) failedCount++;
if(getSalaryInfo.IsFaulted) failedCount++;
return $"{failedCount} tasks failed";
}
});
var resultToReturn = new Response
{
IsuserDetailsSucceeded = userDetails.Result,
IsgetWageInfoSucceeded = getWageInfo.Result,
IsgetSalaryInfoSucceeded = getSalaryInfo.Result,
};
return resultToReturn;
}
I try to convert my code to by async and get more performance. My code need to take items from the database and start 4 tasks with the same item. In the example below, i'm printing the property Offre.TypeOffre.Name. But the problem with the async version is that the navigation property TypeOffre is null and i don't know why. The same code works good in the non-async version.
the TypeOffre object is lazy loaded from EntityFramework. The first one to access the navigation property will send an sql query to the database.
Version before converting
public void AnalyseOffre()
{
var offres = context.Offres.ToList();
var firstOffre = offres.First();
for (var i = 1; i <= 4; i++)
{
DoSomething(firstOffre);
}
}
private void DoSomething(Offre offre)
{
var value = offre.TypeOffre?.Name ?? "null";
Debug.WriteLine(string.Format("value = {0}", value));
}
Result
joe
joe
joe
joe
async version
public async Task AnalyseOffre()
{
var offres = await context.Offres.ToListAsync();
var firstOffre = offres.First();
var tasks = new List<Task>();
for (var i = 1; i <= 4; i++)
{
tasks.Add(Task.Run(() => DoSomething(firstOffre)));
}
await Task.WhenAll(tasks);
}
private async Task DoSomething(Offre offre)
{
var value = offre.TypeOffre?.Name ?? "null";
Debug.WriteLine(string.Format("value = {0}", value));
}
Result
null
null
joe
joe
Hi i'm new in async programming. How can I run my method checkAvaible to run async? I would like to download 2500 pages at once if it's possible, dont wait to complete one download and start another. How can I make it?
private static void searchForLinks()
{
string url = "http://www.xxxx.pl/xxxx/?action=xxxx&id=";
for (int i = 0; i < 2500; i++)
{
string tmp = url;
tmp += Convert.ToString(i);
checkAvaible(tmp); // this method run async, do not wait while one page is downloading
}
Console.WriteLine(listOfUrls.Count());
Console.ReadLine();
}
private static async void checkAvaible(string url)
{
using (WebClient client = new WebClient())
{
string htmlCode = client.DownloadString(url);
if (htmlCode.IndexOf("Brak takiego obiektu w naszej bazie!") == -1)
listOfUrls.Add(url);
}
}
You would not want to download 2500 pages at the same time since this will be a problem for both your client and the server. Instead, I have added a concurrent download limitation (of 10 by default). The web pages will be downloaded 10 page at a time. (Or you can change it to 2500 if you are running a super computer :))
Generic Lists (I think it is a List of strings in your case) is not thread safe by default therefore you should synchronize access to the Add method. I have also added that.
Here is the updated source code to download pages asynhcronously with a configurable amount of concurrent calls
private static List<string> listOfUrls = new List<string>();
private static void searchForLinks()
{
string url = "http://www.xxxx.pl/xxxx/?action=xxxx&id=";
int numberOfConcurrentDownloads = 10;
for (int i = 0; i < 2500; i += numberOfConcurrentDownloads)
{
List<Task> allDownloads = new List<Task>();
for (int j = i; j < i + numberOfConcurrentDownloads; j++)
{
string tmp = url;
tmp += Convert.ToString(i);
allDownloads.Add(checkAvaible(tmp));
}
Task.WaitAll(allDownloads.ToArray());
}
Console.WriteLine(listOfUrls.Count());
Console.ReadLine();
}
private static async Task checkAvaible(string url)
{
using (WebClient client = new WebClient())
{
string htmlCode = await client.DownloadStringTaskAsync(new Uri(url));
if (htmlCode.IndexOf("Brak takiego obiektu w naszej bazie!") == -1)
{
lock (listOfUrls)
{
listOfUrls.Add(url);
}
}
}
}
It's best to convert code to async by working from the inside and proceeding out. Follow best practices along the way, such as avoiding async void, using the Async suffix, and returning results instead of modifying shared variables:
private static async Task<string> checkAvaibleAsync(string url)
{
using (var client = new HttpClient())
{
string htmlCode = await client.GetStringAsync(url);
if (htmlCode.IndexOf("Brak takiego obiektu w naszej bazie!") == -1)
return url;
else
return null;
}
}
You can then start off any number of these concurrently using Task.WhenAll:
private static async Task<string[]> searchForLinksAsync()
{
string url = "http://www.xxxx.pl/xxxx/?action=xxxx&id=";
var tasks = Enumerable.Range(0, 2500).Select(i => checkAvailableAsync(url + i));
var results = await Task.WhenAll(tasks);
var listOfUrls = results.Where(x => x != null).ToArray();
Console.WriteLine(listOfUrls.Length);
Console.ReadLine();
}