Having an issue where my listview source objects are being garbage collected in my app. I have a tabbed page and the default content page has a working list view, and with almost identical code the second content page has another list view but the objects aren't displaying. Is there anyway to prevent them from being garbage collected?
Here is my code behind:
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using MIApp.Classes;
using Xamarin.Forms;
namespace MIApp
{
public partial class VideosListPage : ContentPage
{
public VideosListPage()
{
InitializeComponent();
}
protected async override void OnAppearing()
{
base.OnAppearing();
HttpClient client = new HttpClient();
string url = "https://example.net/api/Videos/GetVideos";
var response = await client.GetAsync(url);
if (response.IsSuccessStatusCode)
{
string res = "";
using (HttpContent content = response.Content)
{
Task<string> result = content.ReadAsStringAsync();
res = result.Result;
var VideosList = Videos.VideosItems.FromJson(res);
VideosListView.ItemsSource = VideosList;
}
}
else
{
await DisplayAlert("Connection Error", "Please Connect to the internet and try again", "Ok");
}
}
}
}
So I am Getting a JSON String from an API that converts entries in a database to a JSON array of objects. I then convert it to a list of objects when I declare VideosList var from a class in which the objects are constructed and deserialised here:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace MIApp.Classes
{
public class Videos
{
public partial class VideosItems
{
[JsonProperty("$id")]
public long Id { get; set; }
[JsonProperty("intVideoID")]
public string IntVideoId { get; set; }
[JsonProperty("strVideoTitle")]
public string StrVideoTitle { get; set; }
[JsonProperty("strVideoURL")]
public string StrVideoURL { get; set; }
[JsonProperty("strVideoPhotoUrl")]
public string StrVideoPhotoUrl { get; set; }
}
public partial class VideosItems
{
public static List<VideosItems> FromJson(string json)
{
return JsonConvert.DeserializeObject<List<VideosItems>>(json);
}
}
}
}
Just FYI, when debugging line by line, when I hover over var Videos List it expands and has the correct count of objects but the resource cannot be found because the objects have been garbage collected.
Thank,
Ryan
Make the VideosList a global variable, it's scope is limited to
using (HttpContent content = response.Content)
{
Task<string> result = content.ReadAsStringAsync();
res = result.Result;
var VideosList = Videos.VideosItems.FromJson(res);
VideosListView.ItemsSource = VideosList;
}
this using block only, so once the compiler goes out of block the VideoList variable gets garbage collected. If you declare that on the class level, it will not happen.
Related
I'm getting this error...
Cannot implicitly convert type 'System.Threading.Tasks.Task System.Collections.Generic.List Thoughts.ViewModel.PickerViewModel.Location'** to **'System.Collections.Generic.List Thoughts.ViewModel.PickerViewModel.Location '
Anyone an idea?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net.Http;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Xamarin.Essentials;
namespace Thoughts.ViewModel
{
public class PickerViewModel
{
public List<Location> LocationsList { get; set; }
public class Location
{
public string name { get; set; }
}
public PickerViewModel()
{
LocationsList = GetLocations();
}
public async Task<JToken> GoogleApi()
{
var location = await Geolocation.GetLastKnownLocationAsync();
string locationString = location.Latitude.ToString() + "," + location.Longitude.ToString();
string radius = "2000";
string apiKey = "My_API_KEY";//Ofcourse its filled in
var httphelper = new HttpClient();
string link = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=" + locationString + "&radius=" + radius + "&key=" + apiKey;
var data = await httphelper.GetStringAsync(link);
var jsonData = JObject.Parse(data)["results"];
return jsonData;
}
public async Task<List<Location>> GetLocations()
{
JToken data = await GoogleApi();
var locationList = new List<Location>() { };
foreach (var location in data)
{
new Location() {name = location["name"].ToString() };
}
return locationList;
}
}
}
A Task<T> is a Task wrapper around a value. Normally, you would unwrap it using await.
However, in this case, the value you're unwrapping is something you want to display in a UI. So here you would want to have your constructor set up something to display in the meantime (an empty collection, or a "Loading..." indicator). Your constructor should then start an asynchronous operation that updates the data to display. This article goes into more details on this pattern.
I need help with uploading files to Slack.
I have a Slack-App that is working with my code(below) so far. But all I can do is post messages. I can not attach images to the messages - because I do not understand how to use the so called "methods" and the syntax Slack is "showing" on their API-page.
This creates my "content" and below its just a Stream for reading a file I could upload:
public class PostMessage
{
public FormUrlEncodedContent Content(string message, string file)
{
var values = new Dictionary<string, string>
{
{"token", "xoxp-myToken"},
{ "username", "X"},
{ "channel", "myChannel"},
{ "as_user", "false"},
{"text", message},
{ "content", file},
{ "attachments","[{ \"fallback\":\"dummy\", \"text\":\"this is a waste of time\"}]"}
};
var content = new FormUrlEncodedContent(values);
return content;
}
}
public class PostFile
{
String path = #"C:\Users\f.held\Desktop\Held-Docs\dagged.jpg";
public string ReadImageFile()
{
FileInfo fileInfo = new FileInfo(path);
long imageFileLength = fileInfo.Length;
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
BinaryReader br = new BinaryReader(fs);
byte[] imageData = br.ReadBytes((int)imageFileLength);
var str = Encoding.Default.GetString(imageData);
return str;
}
}
}
The client that communicates:
public class SlackClient
{
private readonly Uri _webhookUrl;
private readonly HttpClient _httpClient = new HttpClient {};
public SlackClient(Uri webhookUrl)
{
_webhookUrl = webhookUrl;
}
public async Task<HttpResponseMessage> SendMessageAsync(FormUrlEncodedContent content)
{
var response = await _httpClient.PostAsync(_webhookUrl, content);
return response;
}
}
}
The Main:
public static void Main(string[] args)
{
Task.WaitAll(IntegrateWithSlackAsync());
}
private static async Task IntegrateWithSlackAsync()
{
var webhookUrl = new Uri("https://slack.com/api/files.upload");
var slackClient = new SlackClient(webhookUrl);
PostMessage PM = new PostMessage();
PostFile PF = new PostFile();
while (true)
{
Console.Write("Type a message: ");
var message = Console.ReadLine();
var testFile = PF.ReadImageFile();
FormUrlEncodedContent payload = PM.Content(message, testFile);
var response = await slackClient.SendMessageAsync(payload);
var isValid = response.IsSuccessStatusCode ? "valid" : "invalid";
Console.WriteLine($"Received {isValid} response.");
Console.WriteLine(response);
response.Dispose();
}
}
}
}
If somebody has an example on what a upload has to look like. Or even better,
if somebody could really explain the syntax these Slack-Messages have to have.
That would be great! I still do not know where and HOW I should put the so called
"Accepted content types: multipart/form-data, application/x-www-form-urlencoded" to my upload. I just can not find examples on this...
Edit:
What confuses me needlesly is that Slack states they have an extra method called file.upload - but we shouldn't use it anymore, we should use just postMessage.
But how would I "pack" a file in a message? My syntax always seems to be off. Especially when it comes to "content"...
I just can not figure out what the c#-code has to look like. Where do I declare the aforementioned "content type"?
Another problem is, it always sends my messages through - means I get a 200-response from the server. But it never shows the file (which probably means the syntax is off) Or I get the 200-response but the message never shows in Slack.
Images in message
If you want to include an image in your message (along with some text) you can do so by adding images as message attachment to a normal message send with chat.postMessage.
For that you need a public URL of your image and that link with the image_url property to an attachment. That attachment can also contain text, and you can add multiple attachments to your message.
This is how it looks like:
And here is how this message looks in JSON:
{
"channel": "test",
"text": "This is a message example with images in the attachment",
"attachments": [
{
"fallback": "game over",
"text": "This is some text in the attachement",
"image_url": "https://i.imgur.com/jO9N3eJ.jpg"
}
]
}
Uploading images
The image URL needs to be publicly accessible on the Internet. So you need to host your image file on a public webserver or upload it to a image cloud service (e.g. imgur.com).
You can also use Slack as cloud service for your images. Here is how that works:
Upload to Slack: Upload your image to your Slack workspace with files.upload
Get public URL: Get a public URL for your image file with files.sharedPublicURL. Normally all files on Slack are private, but you can only use public URLs for message attachments.
Send message: Include your image as attachment in a message: Use the permalink_public property of your image file as value for image_url
Example code
Here is a full working example in C# for first uploading an image to Slack and then using it in a message.
Note: This example requires Newtonsoft.Json.
using System;
using System.Net;
using System.Collections.Specialized;
using System.Text;
using Newtonsoft.Json;
public class SlackExample
{
// classes for converting JSON respones from API method into objects
// note that only those properties are defind that are needed for this example
// reponse from file methods
class SlackFileResponse
{
public bool ok { get; set; }
public String error { get; set; }
public SlackFile file { get; set; }
}
// a slack file
class SlackFile
{
public String id { get; set; }
public String name { get; set; }
public String permalink_public { get; set; }
}
// reponse from message methods
class SlackMessageResponse
{
public bool ok { get; set; }
public String error { get; set; }
public String channel { get; set; }
public String ts { get; set; }
}
// a slack message attachment
class SlackAttachment
{
public String fallback { get; set; }
public String text { get; set; }
public String image_url { get; set; }
}
// main method with logic
public static void Main()
{
String token = "xoxp-YOUR-TOKEN";
/////////////////////
// Step 1: Upload file to Slack
var parameters = new NameValueCollection();
// put your token here
parameters["token"] = token;
var client1 = new WebClient();
client1.QueryString = parameters;
byte[] responseBytes1 = client1.UploadFile(
"https://slack.com/api/files.upload",
"C:\\Temp\\Stratios_down.jpg"
);
String responseString1 = Encoding.UTF8.GetString(responseBytes1);
SlackFileResponse fileResponse1 =
JsonConvert.DeserializeObject<SlackFileResponse>(responseString1);
String fileId = fileResponse1.file.id;
/////////////////////
// Step 2: Make file public and get the URL
var parameters2 = new NameValueCollection();
parameters2["token"] = token;
parameters2["file"] = fileId;
var client2 = new WebClient();
byte[] responseBytes2 = client2.UploadValues("https://slack.com/api/files.sharedPublicURL", "POST", parameters2);
String responseString2 = Encoding.UTF8.GetString(responseBytes2);
SlackFileResponse fileResponse2 =
JsonConvert.DeserializeObject<SlackFileResponse>(responseString2);
String imageUrl = fileResponse2.file.permalink_public;
/////////////////////
// Step 3: Send message including freshly uploaded image as attachment
var parameters3 = new NameValueCollection();
parameters3["token"] = token;
parameters3["channel"] = "test_new";
parameters3["text"] = "test message 2";
// create attachment
SlackAttachment attachment = new SlackAttachment();
attachment.fallback = "this did not work";
attachment.text = "this is anattachment";
attachment.image_url = imageUrl;
SlackAttachment[] attachments = { attachment };
parameters3["attachments"] = JsonConvert.SerializeObject(attachments);
var client3 = new WebClient();
byte[] responseBytes3 = client3.UploadValues("https://slack.com/api/chat.postMessage", "POST", parameters3);
String responseString3 = Encoding.UTF8.GetString(responseBytes3);
SlackMessageResponse messageResponse =
JsonConvert.DeserializeObject<SlackMessageResponse>(responseString3);
}
}
Here is a shorter working example showing how to just upload any file to Slack with C# only. The example will also automatically share the file the given channel.
I have included the logic to convert the API response from JSON, which will always be needed to determine if the API call was successful.
Note: This example requires Newtonsoft.Json
using System;
using System.Net;
using System.Collections.Specialized;
using System.Text;
using Newtonsoft.Json;
public class SlackExample
{
// classes for converting JSON respones from API method into objects
// note that only those properties are defind that are needed for this example
// reponse from file methods
class SlackFileResponse
{
public bool ok { get; set; }
public String error { get; set; }
public SlackFile file { get; set; }
}
// a slack file
class SlackFile
{
public String id { get; set; }
public String name { get; set; }
}
// main method with logic
public static void Main()
{
var parameters = new NameValueCollection();
// put your token here
parameters["token"] = "xoxp-YOUR-TOKEN";
parameters["channels"] = "test";
var client = new WebClient();
client.QueryString = parameters;
byte[] responseBytes = client.UploadFile(
"https://slack.com/api/files.upload",
"D:\\temp\\Stratios_down.jpg"
);
String responseString = Encoding.UTF8.GetString(responseBytes);
SlackFileResponse fileResponse =
JsonConvert.DeserializeObject<SlackFileResponse>(responseString);
}
}
About content types: Those are part of the header of a HTTP request and can be set manually in the WebClient object (see also this answer). However, for our case you can ignore it, because the default content types that WebClient is using for the POST request will work just fine.
Also see this answer on how to upload files with the WebClient class.
Here is another complete example for uploading a file to Slack, this time using the async approach with HttpClient.
Note: This example requires Newtonsoft.Json.
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
namespace SlackExample
{
class UploadFileExample
{
private static readonly HttpClient client = new HttpClient();
// classes for converting JSON respones from API method into objects
// note that only those properties are defind that are needed for this example
// reponse from file methods
class SlackFileResponse
{
public bool ok { get; set; }
public String error { get; set; }
public SlackFile file { get; set; }
}
// a slack file
class SlackFile
{
public String id { get; set; }
public String name { get; set; }
}
// sends a slack message asynchronous
// throws exception if message can not be sent
public static async Task UploadFileAsync(string token, string path, string channels)
{
// we need to send a request with multipart/form-data
var multiForm = new MultipartFormDataContent();
// add API method parameters
multiForm.Add(new StringContent(token), "token");
multiForm.Add(new StringContent(channels), "channels");
// add file and directly upload it
FileStream fs = File.OpenRead(path);
multiForm.Add(new StreamContent(fs), "file", Path.GetFileName(path));
// send request to API
var url = "https://slack.com/api/files.upload";
var response = await client.PostAsync(url, multiForm);
// fetch response from API
var responseJson = await response.Content.ReadAsStringAsync();
// convert JSON response to object
SlackFileResponse fileResponse =
JsonConvert.DeserializeObject<SlackFileResponse>(responseJson);
// throw exception if sending failed
if (fileResponse.ok == false)
{
throw new Exception(
"failed to upload message: " + fileResponse.error
);
}
else
{
Console.WriteLine(
"Uploaded new file with id: " + fileResponse.file.id
);
}
}
static void Main(string[] args)
{
// upload this file and wait for completion
UploadFileAsync(
"xoxp-YOUR-TOKEN",
"C:\\temp\\Stratios_down.jpg",
"test"
).Wait();
Console.ReadKey();
}
}
}
I am working on a keyword search that looks for a part in two different sites. I have their api and I do get the response.The two responses have different JSONs and different variables in RootObject.
This causes the error
namespace OctopartApi
{
using Newtonsoft.Json;
using RestSharp;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine;
using System.Net;
using UnityEngine.UI;
public class KeywordSearch1 : MonoBehaviour
{
public InputField mainInputField;
public Canvas can;
public float x, y;
void Start () {
mainInputField.onEndEdit.AddListener(delegate {LockInput(mainInputField); });
}
void LockInput(InputField input)
{
ExecuteSearch (input.text);
}
public void ExecuteSearch(string inp)
{
// -- your search query --
string query = inp;
string octopartUrlBase = "http://octopart.com/api/v3";
string octopartUrlEndpoint = "parts/search";
string apiKey = "57af648b";
// Create the search request
var client = new RestClient(octopartUrlBase);
var req = new RestRequest(octopartUrlEndpoint, Method.GET)
.AddParameter("apikey", apiKey)
.AddParameter("q", query)
.AddParameter("start", "0")
.AddParameter("limit", "10");
// Perform the search and obtain results
var resp = client.Execute(req);
string octojson = resp.Content;
RootOb hh = JsonUtility.FromJson<RootOb> (octojson);// RootOb NOT FOUND ( but Root is recognized)
Manufacturer manufacturer = hh.manufacturer;//NOT FOUND
}
// -- your API key -- (https://octopart.com/api/register)
private const string APIKEY = "57af648b";
}
}
This is my model class
[System.Serializable]
public class RootOb
{
public string __class__;
public Brand brand;
public Manufacturer manufacturer;
public string mpn;
public string octopart_url;
public List<Offer> offers;
public List<string> redirected_uids;
public string uid;
}
How I access the response in the second class
Root res = JsonUtility.FromJson<Root>(jsonString);//No issues here
For some reason , I am able to access only Root and not RootOb.What am I doing wrong ?
The type or namespace name `RootOb' could not be found. Are you missing an assembly reference?
Edit : I tried deleting the Root class,and now RootOb gets detected! Should there be only one def of JsonUtility?
New to C# here and to running asynchronous tasks.
I'm trying to scrape some music album info from a website. The webpage's search produces a JSON object in plaintext, but I can't seem to access any DOM info. Here's what I tried (using HtmlAgilityPack):
using HtmlAgilityPack;
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1 {
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
}
public async Task<String> AlbumScraper(string albumname) {
HtmlWeb web = new HtmlWeb();
string albumurl = Uri.EscapeUriString("https://www.metal-archives.com/search/ajax-album-search/?field=title&query=" + albumname);
Console.Write(albumurl);
var albumdoc = await Task.Factory.StartNew(() => web.Load(albumurl));
string albumjson = "";
if (albumdoc.DocumentNode != null) {
albumjson = albumdoc.DocumentNode.InnerText;
}
return albumjson;
}
private async void Form1_Load(object sender, EventArgs e) {
string rawtext = await AlbumScraper("rust+in+peace");
Console.Write(rawtext);
}
}
}
How can I grab the JSON text generated? I can see it clearly when I load the "albumurl" URL...
You can generate it by online tool http://json2csharp.com/
Then add generated class to your code
public class AlbumSearchResponse
{
public string error { get; set; }
public int iTotalRecords { get; set; }
public int iTotalDisplayRecords { get; set; }
public int sEcho { get; set; }
public List<List<string>> aaData { get; set; }
}
convert your response text to class
var data = JsonConvert.DeserializeObject<AlbumSearchResponse>(response);
foreach (var item in data.aaData)
{
//do whatever your want with data
}
also you need add newtonsoft json package from nuget to get JsonConvert working
Firstly you don't need HtmlAgilityPack.
Secondly,try:
using Newtonsoft.Json.Linq;
string albumurl = Uri.EscapeUriString("https://www.metal-archives.com/search/ajax-album-search/?field=title&query=rust+in+peace");
string doc = "";
using (System.Net.WebClient client = new System.Net.WebClient()) // WebClient class inherits IDisposable
{
doc = client.DownloadString(albumurl);
}
then you could deserialize it (#itikhomi)
AlbumSearchResponse data = JsonConvert.DeserializeObject<AlbumSearchResponse>(doc);
You could also parse it manually
JObject json = JObject.Parse(doc);
string error= Convert.ToString(json["error"]);
. . .
string aaData= Convert.ToString(json["aaData"]);
JArray arr = JArray.Parse(aaData);
foreach(JToken token in arr)
{
string[] strarr = token.ToObject<string[]>();
}
Metal archive's album list is provided via API, but the album's details is placed directly in DOM. There is a .NET standard wrapper library for metal archives: MetalArchivesNET.
For now it only allows you to find band/album/song by name, but in the future it will be able to get content by url
My 1st question, so please be kind... :)
I'm using the C# HttpClient to invoke Jobs API Endpoint.
Here's the endpoint: Jobs API Endpoint (doesn't require key, you can click it)
This gives me JSON like so.
{
"count": 1117,
"firstDocument": 1,
"lastDocument": 50,
"nextUrl": "\/api\/rest\/jobsearch\/v1\/simple.json?areacode=&country=&state=&skill=ruby&city=&text=&ip=&diceid=&page=2",
"resultItemList": [
{
"detailUrl": "http:\/\/www.dice.com\/job\/result\/90887031\/918715?src=19",
"jobTitle": "Sr Security Engineer",
"company": "Accelon Inc",
"location": "San Francisco, CA",
"date": "2017-03-30"
},
{
"detailUrl": "http:\/\/www.dice.com\/job\/result\/cybercod\/BB7-13647094?src=19",
"jobTitle": "Platform Engineer - Ruby on Rails, AWS",
"company": "CyberCoders",
"location": "New York, NY",
"date": "2017-04-16"
}
]
}
I've pasted a complete JSON snippet so you can use it in your answer. The full results are really long for here.
Here's are the C# classes.
using Newtonsoft.Json;
using System.Collections.Generic;
namespace MyNameSpace
{
public class DiceApiJobWrapper
{
public int count { get; set; }
public int firstDocument { get; set; }
public int lastDocument { get; set; }
public string nextUrl { get; set; }
[JsonProperty("resultItemList")]
public List<DiceApiJob> DiceApiJobs { get; set; }
}
public class DiceApiJob
{
public string detailUrl { get; set; }
public string jobTitle { get; set; }
public string company { get; set; }
public string location { get; set; }
public string date { get; set; }
}
}
When I invoke the URL using HttpClient and deserialize using JSON.NET, I do get the data back properly.
Here's the code I am calling from my Console App's Main method (hence the static list, I think this could be better refactored??)
private static List<DiceApiJob> GetDiceJobs()
{
HttpClient httpClient = new HttpClient();
var jobs = new List<DiceApiJob>();
var task = httpClient.GetAsync("http://service.dice.com/api/rest/jobsearch/v1/simple.json?skill=ruby")
.ContinueWith((taskwithresponse) =>
{
var response = taskwithresponse.Result;
var jsonString = response.Content.ReadAsStringAsync();
jsonString.Wait();
var result = JsonConvert.DeserializeObject<DiceApiJobWrapper>(jsonString.Result);
if (result != null)
{
if (result.DiceApiJobs.Any())
jobs = result.DiceApiJobs.ToList();
if (result.nextUrl != null)
{
//
// do this GetDiceJobs again in a loop? How?? Any other efficient elegant way??
}
}
});
task.Wait();
return jobs;
}
But now, how do I check if there are more jobs using the nextUrl field? I know I can check to see if it's not null, and if if not, that means there are more jobs to pull down.
Results from my debugging and stepping through
How do I do this recursively, and without hanging and with some delays so I don't cross the API limits? I think I have to use TPL ( Task Parallel Library) but am quite baffled.
Thank you!
~Sean
If you are concerned about response time of your app and would like to return some results before you actually get all pages/data from the API, you could run your process in a loop and also give it a callback method to execute as it gets each page of data from the API.
Here is a sample:
public class Program
{
public static void Main(string[] args)
{
var jobs = GetDiceJobsAsync(Program.ResultCallBack).Result;
Console.WriteLine($"\nAll {jobs.Count} jobs displayed");
Console.ReadLine();
}
private static async Task<List<DiceApiJob>> GetDiceJobsAsync(Action<DiceApiJobWrapper> callBack = null)
{
var jobs = new List<DiceApiJob>();
HttpClient httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://service.dice.com");
var nextUrl = "/api/rest/jobsearch/v1/simple.json?skill=ruby";
do
{
await httpClient.GetAsync(nextUrl)
.ContinueWith(async (jobSearchTask) =>
{
var response = await jobSearchTask;
if (response.IsSuccessStatusCode)
{
string jsonString = await response.Content.ReadAsStringAsync();
var result = JsonConvert.DeserializeObject<DiceApiJobWrapper>(jsonString);
if (result != null)
{
// Build the full list to return later after the loop.
if (result.DiceApiJobs.Any())
jobs.AddRange(result.DiceApiJobs.ToList());
// Run the callback method, passing the current page of data from the API.
if (callBack != null)
callBack(result);
// Get the URL for the next page
nextUrl = (result.nextUrl != null) ? result.nextUrl : string.Empty;
}
}
else
{
// End loop if we get an error response.
nextUrl = string.Empty;
}
});
} while (!string.IsNullOrEmpty(nextUrl));
return jobs;
}
private static void ResultCallBack(DiceApiJobWrapper jobSearchResult)
{
if (jobSearchResult != null && jobSearchResult.count > 0)
{
Console.WriteLine($"\nDisplaying jobs {jobSearchResult.firstDocument} to {jobSearchResult.lastDocument}");
foreach (var job in jobSearchResult.DiceApiJobs)
{
Console.WriteLine(job.jobTitle);
Console.WriteLine(job.company);
}
}
}
}
Note that the above sample allows the callback method to access each page of data as it is received by the GetDiceJobsAsync method. In this case, the console, displays each page as it becomes available. If you do not want the callback option, you can simply pass nothing to GetDiceJobsAsync.
But the GetDiceJobsAsync also returns all the jobs when it completes. So you can choose to act on the whole list at the end of GetDiceJobsAsync.
As for reaching API limits, you can insert a small delay within the loop, right before you repeat the loop. But when I tried it, I did not encounter the API limiting my requests so I did not include it in the sample.