JsonConvert - Can not assign string value to a string variable - c#

public void FindCityName()
{
string url = "http://maps.google.com/maps/geo?q=39.920794,32.853902&output=json&oe=utf8&sensor=true&key=MYKEY";
var w = new WebClient();
Observable.FromEvent<DownloadStringCompletedEventArgs>(w, "DownloadStringCompleted").Subscribe(r =>
{
var deserialized = JsonConvert.DeserializeObject<RootObject>(r.EventArgs.Result);
string s = deserialized.Placemark[0].AddressDetails.Country.SubAdministrativeArea.Locality.LocalityName;
/// setCity() and City=s produce the same thing
setCity(s);
City = s;
//foreach (var item in deserialized.Placemark)
//{
// //MessageBox.Show(item.AddressDetails.Country.SubAdministrativeArea.Locality.LocalityName);
// City = (string)item.AddressDetails.Country.SubAdministrativeArea.Locality.LocalityName;
//}
//Problem here >>>>>
////MessageBox.Show(City);
});
w.DownloadStringAsync(new Uri(url));
}
Problem:
I am working on a windows phone 7 application and I need to find the "City Name" from GPS coordinates in order to move forward...
I found the code above on the internet and tried it. I can see the city name by using these codes(Message.Box(City) show exactly what I want, the city name). However, this line of code
deserialized.Placemark[0].AddressDetails.Country.SubAdministrativeArea.Locality.LocalityName;
which gives me the city name seems to give a volatile string value.
For example, I created a method which assigns the value of string variable "s" to the string field of my class, name City. If I try to get the City's content after calling FindCityName() method, I see that City's content is not updated.
Again, same thing happens then I call the code line under the comment "Problem here >>>>>" that MessageBox.Show(City) shows nothing new...
Can someone explain me the reason of my problem?

you put this question on my blog as well, but I will answer it here. I feel a bit responsible for putting up the sample code in the first place ;-)
I am going to assume the class containing your code looks like this:
public class MyClass
{
private void MyMethod()
{
FindCityName();
MessageBox.Show(City);
}
private void FindCityName()
{
// Code omitted - see your question
}
private string City;
}
There is nothing volatile about the string. Your problem is asynchronicity. If you look carefully you will see that I use an observable that fires when the DownloadStringCompleted is fired. The code inside Observable.Event is only called when the download is finished but that happens asynchronously. But what I assume you do is call the FindCityName method and then directly trying to access results like I show in the MyMethod method. That's like directly wanting the result after firing the request. The results are not in yet! It's like a web page downloading - it takes a while. You can fix that with a callback, something like this:
public class MyClass
{
private void MyMethod()
{
FindName();
}
public void FindCityName()
{
string url = "http://maps.google.com/maps/geo?q=39.920794,32.853902&output=json&oe=utf8&sensor=true&key=MYKEY";
var w = new WebClient();
Observable.FromEvent<DownloadStringCompletedEventArgs>(w, "DownloadStringCompleted").Subscribe(r =>
{
var deserialized = JsonConvert.DeserializeObject<RootObject>(r.EventArgs.Result);
City = deserialized.Placemark[0].AddressDetails.Country.SubAdministrativeArea.Locality.LocalityName;
DoneDownloading();
});
w.DownloadStringAsync(new Uri(url));
}
private string City;
private void DoneDownloading
{
MessageBox.Show(City);
}
}
Does that help?

I would recommend you to use this Google Map API
http://maps.googleapis.com/maps/api/geocode/json?latlng=39.920794,32.853902&sensor=true
And once you get JSON response in your request. You can parse easily with NEWTONSOFT for wp7
WebClient wc = new WebClient();
var json = (JObject)JsonConvert.DeserializeObject(wc.DownloadString(url));
var locality= json["results"]
.SelectMany(x => x["address_components"])
.FirstOrDefault(t => t["types"].First().ToString() == "locality");
var name = locality!=null ? locality["long_name"].ToString() : "";

Related

Xamarin ZXing Qr to List

In my project i need contunistly read barcode and put them in a list. But while i trying it its always giving me NullReferenceException but i didn't get it. When i check it in debug i can see it have a string value but while trying put it to list its always giving me that error. Its my code for Scan and take data :
List<HoldMyString> mylist;
HoldMyString hold;
private void ZXingScannerView_OnScanResult(ZXing.Result result)
{
Device.BeginInvokeOnMainThread(() =>
{
hold.HoldString = result.Text; // HoldString is taking string value
mylist.Add(hold);
});
}
How i can put my datas in to my list contunistly. Thanks for help!
I was able to reproduce the same error with yours.
You could use the code below.
HoldMyString:
public class HoldMyString
{
public string HoldString { get; set; }
}
Code for Scan and take data:
List<HoldMyString> mylist=new List<HoldMyString>();
HoldMyString hold = new HoldMyString();
private void Handle_OnScanResult(ZXing.Result result)
{
Device.BeginInvokeOnMainThread(() =>
{
hold.HoldString = result.Text; // HoldString is taking string value
mylist.Add(hold);
});
}
For more details about how to get the barcodes, you could check the thread i done before.
How to embed ZXing scanner in PageRenderer/Fragment/View into Xamarin.Forms ContentPage?

Appending chat history and insert it into the same file

I have a case that I need to log the chat history (I am capable of doing this already) and I need it to be logged in a text file (able to log it already).
The problem is the file is being accessed all over again so I need to somewhere store the filename of the file somewhere else, right now I have this code:
public async Task LogAsync(IActivity activity)
{
var conversation = "";
var convActivity = "";
var ctr = 0;
conversation = $"From: {activity.From.Name}\r\n To: {activity.Recipient.Name}\r\n Message: {activity.AsMessageActivity()?.Attachments}\r\n ";
fileName = "test";
await LogActivity(fileName, conversation);
}
The LogActivity is the one handling the append of the file. So what I need is I want the unique fileName to be instantiated once while appending the file all over again or rather while continuously accessing this method.
Or is there a way to log the chat history of bot once like if a Context.Done was called or before it?
Or the inefficient way I am thinking of was making use of .From.Name and .Recipient.Name
So the result will be:
if (activity.From.Name.ToLower().ToString() == "user")
{
name.Value = $"{activity.From.Name.ToString()}";
conversation = $"From: {activity.From.Name}\r\n To: {activity.Recipient.Name}\n Message: {activity.AsMessageActivity()?.Text}\n";
}
else
{
name.Value = $"{activity.Recipient.Name.ToString()}";
conversation = $"From: {activity.From.Name}\r\n To: {activity.Recipient.Name}\r\n Message: {activity.AsMessageActivity()?.Text}\r\n ";
}
await LogActivity(name.Value, conversation);
If I understand correctly, you just want to persist a value throughout a conversation; in this case a filename.
If that's correct, then you can store it in PrivateConversationData which lives in thecontext.
For example:
context.PrivateConversationData.SetValue<string>("log_filename", "log-name-here.txt");
For an example, check here: https://www.robinosborne.co.uk/2016/08/08/persisting-data-within-a-conversation-with-botframeworks-dialogs/
For a full example about persisting the whole conversation, this might also help: https://www.robinosborne.co.uk/2016/11/22/transcribing-messages-in-botframework/
Okay, rposbo's answer also works if you have your own logger (that's what I observed, or maybe if you can implement it the other way around, you can use it, it's up to you) that will persist on each every conversation you have with your bot, see the link he provided on how to persist the whole conversation. As for my end, I used dictionary to store the filename. So down below is what I did
public string _Name { get { return name; } }
string name;
public static Dictionary<string, string> fileName = new Dictionary<string, string>();
public void SetFileName(string _fileName)
{
var isCached = fileName.TryGetValue("filename", out name);
if (!isCached)
{
name = $"{_fileName}_{DateTime.Now.Ticks}";
fileName.Add("filename", name);
}
}
Btw, can I accept two answers? since rposbo's answer also works, but it just doesn't fit for me.

Converting large httpresponse stream to a JSON string?

Question Background:
Update:
I'm still not sure how to go about extracting the relevant information from this response. I have tried setting my response type to JSON but still receive the response as shown below. I have taken into account what has been said in regards to using NameValueCollection but still cant see how this will help with such a large response. Ideally I'd like this mapped to an object structure of some kind, it dosen't necessarily have to be JSON.
Question:
I'm currently using the PayPal Api 'ExpressCheckout' method to allow users to pay for items on my test site. A HTTP response from the API provides a large response containing key information I need to extract - such as the buyers address, if the payment was succesful etc.
The Issue:
Currently I'm stuck on how to work with the response. Ideally I'd convert the data to a JSON string then use Newtonsoft to map the data to C# classes thus allowing easy access to the data. Here is an example of the Httpresponse:
TOKEN=EC%2XXXXXXXXXXXXXXXXXR&BILLINGAGREEMENTACCEPTEDSTATUS=0&CHECKOUTSTATUS=PaymentActionNotInitiated&TIMESTAMP=2015%2d01%2d02T21%3a11%3a30Z&CORRELATIONID=ab184fdba7a30&ACK=Success&VERSION=88%2e0&BUILD=14443165&EMAIL=test%40aol%2ecom&PAYERID=3XXXXXXXXXX4N&PAYERSTATUS=verified&BUSINESS=Test%20Biz%27s%20Test%20Store&FIRSTNAME=Joe&LASTNAME=King&COUNTRYCODE=GB&SHIPTONAME=Joe%20King%27s%20Test%20Store&SHIPTOSTREET=1%20Main%20Terrace&SHIPTOCITY=Wolverhampton&SHIPTOSTATE=West%20Midlands&SHIPTOZIP=W12%204LQ&SHIPTOCOUNTRYCODE=GB&SHIPTOCOUNTRYNAME=United%20Kingdom&ADDRESSSTATUS=Confirmed&CURRENCYCODE=GBP&AMT=15%2e56&ITEMAMT=15%2e56&SHIPPINGAMT=0%2e00&HANDLINGAMT=0%2e00&TAXAMT=0%2e00&INSURANCEAMT=0%2e00&SHIPDISCAMT=0%2e00&L_NAME0=ItemOne&L_QTY0=4&L_TAXAMT0=0%2e00&L_AMT0=3%2e89&L_ITEMWEIGHTVALUE0=%20%20%200%2e00000&L_ITEMLENGTHVALUE0=%20%20%200%2e00000&L_ITEMWIDTHVALUE0=%20%20%200%2e00000&L_ITEMHEIGHTVALUE0=%20%20%200%2e00000&PAYMENTREQUEST_0_CURRENCYCODE=GBP&PAYMENTREQUEST_0_AMT=15%2e56&PAYMENTREQUEST_0_ITEMAMT=15%2e56&PAYMENTREQUEST_0_SHIPPINGAMT=0%2e00&PAYMENTREQUEST_0_HANDLINGAMT=0%2e00&PAYMENTREQUEST_0_TAXAMT=0%2e00&PAYMENTREQUEST_0_INSURANCEAMT=0%2e00&PAYMENTREQUEST_0_SHIPDISCAMT=0%2e00&PAYMENTREQUEST_0_INSURANCEOPTIONOFFERED=false&PAYMENTREQUEST_0_SHIPTONAME=Joe%20King%27s%20Test%20Store&PAYMENTREQUEST_0_SHIPTOSTREET=1%20Main%20Terrace&PAYMENTREQUEST_0_SHIPTOCITY=Wolverhampton&PAYMENTREQUEST_0_SHIPTOSTATE=West%20Midlands&PAYMENTREQUEST_0_SHIPTOZIP=W12%204LQ&PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE=GB&PAYMENTREQUEST_0_SHIPTOCOUNTRYNAME=United%20Kingdom&PAYMENTREQUEST_0_ADDRESSSTATUS=Confirmed&L_PAYMENTREQUEST_0_NAME0=ItemOne&L_PAYMENTREQUEST_0_QTY0=4&L_PAYMENTREQUEST_0_TAXAMT0=0%2e00&L_PAYMENTREQUEST_0_AMT0=3%2e89&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE0=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE0=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE0=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE0=%20%20%200%2e00000&PAYMENTREQUESTINFO_0_ERRORCODE=0
If anyone could give me an easy way to map this data to a C# object that would be great.
Is there any specific reason why you want it in JSON format? If its not requirement and if you can live with key value pair then here is one way you can process response as key value pair.
public partial class Form1 : Form
{
Dictionary<string, string> processedResponse = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string rawResponse = "TOKEN=EC%2XXXXXXXXXXXXXXXXXR&BILLINGAGREEMENTACCEPTEDSTATUS=0&CHECKOUTSTATUS=PaymentActionNotInitiated&TIMESTAMP=2015%2d01%2d02T21%3a11%3a30Z&CORRELATIONID=ab184fdba7a30&ACK=Success&VERSION=88%2e0&BUILD=14443165&EMAIL=test%40aol%2ecom&PAYERID=3XXXXXXXXXX4N&PAYERSTATUS=verified&BUSINESS=Test%20Biz%27s%20Test%20Store&FIRSTNAME=Joe&LASTNAME=King&COUNTRYCODE=GB&SHIPTONAME=Joe%20King%27s%20Test%20Store&SHIPTOSTREET=1%20Main%20Terrace&SHIPTOCITY=Wolverhampton&SHIPTOSTATE=West%20Midlands&SHIPTOZIP=W12%204LQ&SHIPTOCOUNTRYCODE=GB&SHIPTOCOUNTRYNAME=United%20Kingdom&ADDRESSSTATUS=Confirmed&CURRENCYCODE=GBP&AMT=15%2e56&ITEMAMT=15%2e56&SHIPPINGAMT=0%2e00&HANDLINGAMT=0%2e00&TAXAMT=0%2e00&INSURANCEAMT=0%2e00&SHIPDISCAMT=0%2e00&L_NAME0=ItemOne&L_QTY0=4&L_TAXAMT0=0%2e00&L_AMT0=3%2e89&L_ITEMWEIGHTVALUE0=%20%20%200%2e00000&L_ITEMLENGTHVALUE0=%20%20%200%2e00000&L_ITEMWIDTHVALUE0=%20%20%200%2e00000&L_ITEMHEIGHTVALUE0=%20%20%200%2e00000&PAYMENTREQUEST_0_CURRENCYCODE=GBP&PAYMENTREQUEST_0_AMT=15%2e56&PAYMENTREQUEST_0_ITEMAMT=15%2e56&PAYMENTREQUEST_0_SHIPPINGAMT=0%2e00&PAYMENTREQUEST_0_HANDLINGAMT=0%2e00&PAYMENTREQUEST_0_TAXAMT=0%2e00&PAYMENTREQUEST_0_INSURANCEAMT=0%2e00&PAYMENTREQUEST_0_SHIPDISCAMT=0%2e00&PAYMENTREQUEST_0_INSURANCEOPTIONOFFERED=false&PAYMENTREQUEST_0_SHIPTONAME=Joe%20King%27s%20Test%20Store&PAYMENTREQUEST_0_SHIPTOSTREET=1%20Main%20Terrace&PAYMENTREQUEST_0_SHIPTOCITY=Wolverhampton&PAYMENTREQUEST_0_SHIPTOSTATE=West%20Midlands&PAYMENTREQUEST_0_SHIPTOZIP=W12%204LQ&PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE=GB&PAYMENTREQUEST_0_SHIPTOCOUNTRYNAME=United%20Kingdom&PAYMENTREQUEST_0_ADDRESSSTATUS=Confirmed&L_PAYMENTREQUEST_0_NAME0=ItemOne&L_PAYMENTREQUEST_0_QTY0=4&L_PAYMENTREQUEST_0_TAXAMT0=0%2e00&L_PAYMENTREQUEST_0_AMT0=3%2e89&L_PAYMENTREQUEST_0_ITEMWEIGHTVALUE0=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMLENGTHVALUE0=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMWIDTHVALUE0=%20%20%200%2e00000&L_PAYMENTREQUEST_0_ITEMHEIGHTVALUE0=%20%20%200%2e00000&PAYMENTREQUESTINFO_0_ERRORCODE=0";
//Process response
processedResponse = ProcessResponse(rawResponse);
//Use received data e.g.
//Get First name
string fName = GetRecordValue("FIRSTNAME");
//Get Last name
string lName = GetRecordValue("LASTNAME");
}
private Dictionary<string,string> ProcessResponse(string response)
{
Dictionary<string, string> responseData = new Dictionary<string, string>();
if(!string.IsNullOrWhiteSpace(response))
{
string[] firstPass = response.Split(new char[] { '&' });
foreach(string pair in firstPass)
{
string[] secondPass = pair.Split(new char[] { '=' });
if(secondPass!=null && secondPass.Length>0)
{
responseData.Add(secondPass[0].Trim(), secondPass[1].Trim());
}
}
}
return responseData;
}
private string GetRecordValue(string record)
{
string recordValue = null;
if(processedResponse!=null)
{
if(!string.IsNullOrWhiteSpace(record) && processedResponse.ContainsKey(record))
{
recordValue= processedResponse[record];
}
}
return recordValue;
}
}
Still unsure what's the problem with dealing with it as a NameValueCollection.
E.g.
//NameValueCollection
//or use HttpUtility.ParseQueryString(some_string_of_names_values)
var foo = Request.QueryString;
var bar = foo["FIRSTNAME"]; //based on above this is "Joe"
Update:
It is response string which is being processed here and not request hence you wont be able to retrieve details using Request.QueryString
As stated in comments:
string rawResponse = "TOKEN=EC%2XXXXXXXXXXXXXXXXXR&BILLINGAGREEMENTACCEPTEDSTATUS=0&CHECKOUTSTATUS=PaymentActionNotInitiated&TIMESTAMP=2015%2d01%2d02T21%3a11%3a30Z&CORRELATIONID=ab184fdba7a30&ACK=Success&VERSION=88...."
var foo = HttpUtility.ParseQueryString(rawResponse); //NameValueCollection
var bar = foo["FIRSTNAME"]; //Joe

2 asynchronus download requests, but the second is dependent on the result of the first - how to sync

Edit: Sorry - now that I've understood the problem a bit better, I think my problem lies elsewhere
I have 2 asynchronus requests.
The first is this:
public void DownloadWebData(Uri apiUrl)
{
WebClient client = new WebClient();
client.DownloadDataCompleted += DownloadDataCompleted;
client.DownloadDataAsync(apiUrl);
}
public void DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
{
string result = System.Text.Encoding.UTF8.GetString (e.Result);
Uri downloadLink = (GetUri(result));
}
Basically it makes a simple url based API request to a remote webserver which returns some basic textual data over http. GetUri() just parses that data to extract an address from the data for an image to download.
I'm then using imageLoader in monotouch.dialog to download the image. All code is in the same class.
Edit: added the imageLoader code (I left the Console lines in because they serve reasonably well as comments).
public void downloadImage (Uri imageUri)
{
var tmp_img = ImageLoader.DefaultRequestImage (imageUri, this);
if (tmp_img != null)
{
adView.Image = tmp_img;
Console.WriteLine ("Image already cached, displaying");
}
else
{
adView.Image = UIImage.FromFile ("Images/downloading.jpg");
Console.WriteLine ("Image not cached. Using placeholder.");
}
}
public void UpdatedImage (System.Uri uri)
{
adView.Image = ImageLoader.DefaultRequestImage(uri, this);
}
You missed to check if e.Result actually contains something. The download might as well have failed and e.Result is null. Add some basic error handling to your code.
if you are using DownloadWebData inside a for loop, it will be better you generate seperate functions for DownloadDataCompleted event.
You can use anonymous function inside DownloadWebData().
client.DownloadDataCompleted +=(s,e)=>{
string result = System.Text.Encoding.UTF8.GetString (e.Result);
Uri downloadLink = (GetUri(result));
};
After realizing I was asking the wrong question, I finally figured it out here:
Hand back control to main UI thread to update UI after asynchronus image download

Looking for OO gurus, need some help in the design of my programming logic. Nothing fancy, just new to it

I'll post my entire class and maybe someone with MUCH more experience can help me design something better. I'm really new to doing things Asynchronously, so I'm really lost here. Hopefully my design isn't TOO bad. :P
IMDB Class:
public class IMDB
{
WebClient WebClientX = new WebClient();
byte[] Buffer = null;
public string[] SearchForMovie(string SearchParameter)
{
//Format the search parameter so it forms a valid IMDB *SEARCH* url.
//From within the search website we're going to pull the actual movie
//link.
string sitesearchURL = FindURL(SearchParameter);
//Have a method download asynchronously the ENTIRE source code of the
//IMDB *search* website, and save it to the byte[] "Buffer".
WebClientX.DownloadDataCompleted += new DownloadDataCompletedEventHandler(WebClientX_DownloadDataCompleted);
WebClientX.DownloadDataAsync(new Uri(sitesearchURL));
//Convert the byte[] to a string so we can easily find the *ACTUAL*
//movie URL.
string sitesearchSource = Encoding.ASCII.GetString(Buffer);
//Pass the IMDB source code to method FindInformation() to FIND the movie
//URL.
string MovieURL = FindMovieURL(sitesearchSource);
//Download the source code from the recently found movie URL.
WebClientX.DownloadDataAsync(new Uri(MovieURL));
//Convert the source code to readable string for scraping of information.
string sitemovieSource = Encoding.ASCII.GetString(Buffer);
string[] MovieInformation = ScrapeInformation(sitemovieSource);
return MovieInformation;
}
void WebClientX_DownloadDataCompleted(object sender, DownloadDataCompletedEventArgs e)
{
Buffer = e.Result;
throw new NotImplementedException();
}
/// <summary>
/// Formats a valid IMDB url for ease of use according to a search parameter.
/// </summary>
/// <param name="sitesearchSource"></param>
/// <returns></returns>
private string FindMovieURL(string sitesearchSource)
{
int Start = sitesearchSource.IndexOf("<link rel=\"canonical\" href=\"");
string IMDBCode = sitesearchSource.Substring((Start + 28), 35);
return IMDBCode;
}
private string[] ScrapeInformation(string Source)
{
string[] Information = new string[5];
Information[0] = FindTitle(Source);
Information[1] = FindDirector(Source);
Information[2] = FindYear(Source);
Information[3] = FindPlot(Source);
Information[4] = FindPoster(Source);
return Information;
}
/*************************************************************************/
private string FindURL(string Search)
{
string[] SearchArray = Search.Split(' ');
string FormattedQuery = "";
foreach (string X in SearchArray)
{
FormattedQuery += X + "+";
}
FormattedQuery.Remove((FormattedQuery.Length - 1), 1);
string TheFormattedQuery = "http://www.imdb.com/find?s=all&q=" + FormattedQuery + "&x=0&y=0";
return TheFormattedQuery;
}
private string FindTitle(string Source)
{
//<title>Couples Retreat (2009)</title>
int Start = Source.IndexOf("<title>");
string Bookmark = Source.Substring((Start + 7), 400);
int End = Bookmark.IndexOf("</title>");
string Title = Bookmark.Substring(0, End - 7);
return Title;
}
private string FindDirector(string Source)
{
int Start = Source.IndexOf("<h5>Director:</h5>");
string Bookmark = Source.Substring((Start + 18), 250);
Start = Bookmark.IndexOf(">");
Bookmark = Bookmark.Substring(Start + 1, 100);
int End = Bookmark.IndexOf("</a>");
string Director = Bookmark.Substring(0, End - 1);
return Director;
}
private string FindYear(string Source)
{
int Start = Source.IndexOf("<h5>Release Date:</h5>");
string Bookmark = Source.Substring((Start + 22), 40);
int End = Bookmark.IndexOf("<a class=");
string ReleaseYear = Bookmark.Substring(0, End - 1);
return ReleaseYear;
}
private string FindPlot(string Source)
{
int Start = Source.IndexOf("<h5>Plot:</h5>");
string Bookmark = Source.Substring((Start + 14), 700);
int End = Bookmark.IndexOf("<a class");
string Plot = Bookmark.Substring(0, End - 1);
return Plot;
}
private string FindPoster(string Source)
{
int Start = Source.IndexOf("<a name=\"poster\" href=");
string Bookmark = Source.Substring((Start + 22), 700);
Start = Bookmark.IndexOf("src=\"");
string PosterURL = Bookmark.Substring((Start + 5), 103);
return PosterURL;
}
}
Form1.cs Class (My windows Form):
public partial class MainSearchForm : Form
{
public MainSearchForm()
{
InitializeComponent();
}
SearchFunctions.IMDB IMDBClass = new QuickFlick.SearchFunctions.IMDB();
int YPosition = 5;
private void btnSearch_Click(object sender, EventArgs e)
{
string[] MovieInformation = IMDBClass.SearchForMovie(txtSearch.Text);
LoadMovieInformation(MovieInformation);
}
public void LoadMovieInformation(string[] FoundInfo)
{
MovieItem TheMovieItem = new MovieItem();
TheMovieItem.SetTitle(FoundInfo[0]);
TheMovieItem.SetDirector(FoundInfo[1]);
TheMovieItem.SetRelease(FoundInfo[2]);
TheMovieItem.SetPlot(FoundInfo[3]);
TheMovieItem.SetPoster(FoundInfo[4]);
TheMovieItem.Location = new Point(5, YPosition);
YPosition += 196;
panel1.Controls.Add(TheMovieItem);
}
}
Now, the gist of what my program is trying to accomplish.
A user will write the name down of a movie, and I'll pull up the information about it. Nothing else! :P It's mostly intended for me to learn Async functions etc. but I'm scared I might be approaching this the completely wrong way.
Once again, I'm not looking for much code, just design of the program. Methods, order of methods, unnecesary methods, etc. :D
Thanks a bunch SO, as always, you rock!
I'm not going to comment on the entire design because, like #Vinko, I think you ought to focus your question a little more in that respect. I will say though that you are fundamentally misunderstanding how the asynchronous methods work. They will return immediately to the calling thread without completing first. That's the whole point with asynchronous threads - they don't wait until they have finished before they return.
For this to work, you must either use synchronous calls or wait on some event after the call and signal that event in the asynchronous callback handler so that your code doesn't attempt to access the returned data until after the call has completed. The benefit in this approach is that your thread is free to perform other tasks while waiting on the call to complete, but you must absorb the complexity of managing the waiting process (it's not much).
See the sample code for the DownloadDataCompleted event at MSDN. You'll need to do a wait for each of the web client downloads that you do. Note that you need to invoke the Set() method on the AutoResetEvent object passed to your handler in the example in your handler.
You need to have models.
For example the FindTitle() and FindDirector() methods should be in a separate class.. perhaps called 'Movie' or 'IMDBMovie'. This class should have attributes such as 'title' and 'director' with getters / setters.
This article could prove helpful to you: MVC
I would agree with clownbaby on the need for models, but would disagree on the need for FindTitle, FindDirectory etc. needing to be in separate classes.
I think that you should structure your code as follows:
Have a class that takes in the search parameters in a generic way and then calls a search through some kind of factory class.
Have the factory class internally create a specialized version of the search class based on either a specification by the user, or a specification in the configuration settings of the application.
Have the search class format the search query and then call another class which simply makes the HTTP request and gets back the HTTP response contents to the search class.
Have the search class then parse the HTTP response to generate the results to be fed back to the class that called the search in Item 1.
The advantages of this sort of approach are that you can then isolate the code that is specialized for processing website specific parsing routines into it's own class and isolate it from the UI as well as utilize a single class to make the HTTP request, regardless of how many websites you want to scrape.
Put another way, the View layer should not care how exactly the data is retrieved and parsed and should have the flexibility to call on various sources of information.
Likewise, the Data layer should not care how the data should be processed and should only be concerned with where to get the data from.
The Domain layer is the one that will sit in the middle, take raw data from the Data layer, parse it and make sense of it and then format it into a shape that the View can understand.
This way, if say you want to also target Yahoo! Movies, Wikipedia or any of the other sources of movie data, you can do that. All you would have to do is add another class in the Domain layer and inform the View that a new source is available in the Domain layer.
Good design is about limiting the impact of changes so that additional features can be built into the application.
Good design is also about being able to test individual components. A design such as the one I have described will allow you to test the various classes that comprise individual layers and ensure that they work as advertised.
Something I find very useful is that if a class cannot be tested very easily using NUnit and Rhino.Mocks, it's probably not very well-designed.
Better to split the model object from the data retrieval object.
User sees View, uses btnSearch_Click
Search starts on other thread, btnSearch_Click action finishes
Search finishes, data may be available, new object creation and addition to view.
View has new data event and redisplays content.
Some adjusted code to do the async calls.
public string[] SearchForMovie(string SearchParameter)
{
//Format the search parameter so it forms a valid IMDB *SEARCH* url.
//From within the search website we're going to pull the actual movie
//link.
string sitesearchURL = FindURL(SearchParameter);
//Have a method download asynchronously the ENTIRE source code of the
//IMDB *search* website, and save it to the byte[] "Buffer".
WebClientX.DownloadDataCompleted += new DownloadDataCompletedEventHandler(WebClientX_DownloadSearchCompleted);
WebClientX.DownloadDataAsync(new Uri(sitesearchURL));
}
void WebClientX_DownloadSearchCompleted(object sender, DownloadDataCompletedEventArgs e)
{
Buffer = e.Result;
//Convert the byte[] to a string so we can easily find the *ACTUAL*
//movie URL.
string sitesearchSource = Encoding.ASCII.GetString(Buffer);
//Pass the IMDB source code to method FindInformation() to FIND the movie
//URL.
string MovieURL = FindMovieURL(sitesearchSource);
//Download the source code from the recently found movie URL.
WebClientX.DownloadDataCompleted += new DownloadDataCompletedEventHandler(WebClientX_DownloadMovieCompleted);
WebClientX.DownloadDataAsync(new Uri(MovieURL));
}
void WebClientX_DownloadMovieCompleted(object sender, DownloadDataCompletedEventArgs e)
{
Buffer = e.Result;
//Convert the source code to readable string for scraping of information.
string sitemovieSource = Encoding.ASCII.GetString(Buffer);
// would create a movie object here rather than have the scrape function on this class
string[] MovieInformation = ScrapeInformation(sitemovieSource);
Model.LoadMovieInformation(MovieInformation);
}

Categories

Resources