Jak już wspominałem w poprzednim temacie robię sobie mały projekt, który ma za zadanie wyciągać pewne dane ze strony internetowej. Problem w tym, że aby pracować na pobranych danych muszę je najpierw pobrać a to chwilę trwa. W związku z tym chciałbym się zapytać jak zagwarantować, że jakaś część kodu może się zacząć wykonywać dopiero po wykonaniu innej części/metody? Na razie jakoś udało mi się to obejść, ale pewnie jest to bardzo złe rozwiązanie i wolałbym to zrobić w jakiś lepszy sposób. Próbowałem się bawić Taskami, ale jeszcze tego do końca nie ogarnąłem.
Klasa pobierająca dane:
public class DivPuller
{
private static string websiteAddress=null;
private static string result = null;
public DivPuller(string website)
{
websiteAddress = website;
}
public async Task<string> ExtractStringFromDiv()
{
HttpClient http = new HttpClient();
var response = await http.GetByteArrayAsync(websiteAddress);
String source = Encoding.GetEncoding("UTF-8").GetString(response, 0, response.Length - 1);
source = WebUtility.HtmlDecode(source);
HtmlDocument resultDocument = new HtmlDocument();
resultDocument.LoadHtml(source);
List<HtmlNode> teamslist =
resultDocument.DocumentNode.Descendants()
.Where(
x =>
(x.Name == "div" && x.Attributes["class"] != null &&
x.Attributes["class"].Value.Contains("box-shiny-alt")))
.ToList();
var node = teamslist[0];
result = node.InnerText;
return result;
}
public string GetResult()
{
return result;
}
}
Klasa parsująca pobrane dane:
public class DataParser
{
private string ToBeParsed = null;
private string TeamOneName = null;
private string TeamTwoName = null;
private string WinningTeamName = null;
private int TeamOneOdds = 0;
private int TeamTwoOdds = 0;
private string MatchFormat = null;
public DataParser(string tobeparsed)
{
this.ToBeParsed = tobeparsed;
ToBeParsed = Regex.Replace(ToBeParsed, @"[^\u0020-\u007E]", string.Empty);
string newWord = ToBeParsed.Replace(" ", String.Empty);
int start = newWord.IndexOf("CET") + "CET".Length;
int end = newWord.LastIndexOf('%');
string wynik = newWord.Substring(start, end - start);
//pelen wynik meczu z oddsami i nazwami teamow
string wynik1 = wynik.Replace("%", string.Empty);
int format = newWord.IndexOf("Bestof");
string matchFormat = newWord.Substring(format + "Bestof".Length);
//gotowy format meczu
string matchFormatResult = matchFormat.Substring(0, 1);
MatchFormat = matchFormatResult;
string team1withodds = wynik1.Split(new string[] { "vs" }, StringSplitOptions.None)[0];
string team2withodds = wynik1.Split(new string[] { "vs" }, StringSplitOptions.None)[1];
//wychwytuje oddsy team1
Match myMatch;
MatchCollection matchesCollection = Regex.Matches(team1withodds, @"\d+");
if (matchesCollection.Count > 1)
{
myMatch = matchesCollection[1];
}
else
{
myMatch = matchesCollection[0];
}
TeamOneOdds = int.Parse(myMatch.Value);
//wychwytuje oddsy team2
Match myMatch1;
MatchCollection matchesCollection1 = Regex.Matches(team2withodds, @"\d+");
if (matchesCollection1.Count > 1)
{
myMatch1 = matchesCollection1[1];
}
else
{
myMatch1 = matchesCollection1[0];
}
TeamTwoOdds = int.Parse(myMatch1.Value);
int team1odds = Int32.Parse(Regex.Match(team1withodds, @"\d+").Value);
int team2odds = Int32.Parse(Regex.Match(team2withodds, @"\d+").Value);
string team1name = team1withodds.Replace(myMatch.Value, "");
string team2name = team2withodds.Replace(myMatch1.Value, "");
string winningteam = "Tie";
string team1wowin;
string team2wowin;
//wywalenie wina z nazw
if (team1name.Contains("(win)"))
{
team1wowin = team1name.Replace("(win)", "");
winningteam = team1wowin;
}
else
{
team1wowin = team1name;
}
if (team2name.Contains("(win)"))
{
team2wowin = team2name.Replace("(win)", "");
winningteam = team2wowin;
}
else
{
team2wowin = team2name;
}
TeamOneName = team1wowin;
TeamTwoName = team2wowin;
WinningTeamName = winningteam;
}
public string GetTeamOneName()
{
return TeamOneName;
}
public string GetTeamTwoName()
{
return TeamTwoName;
}
public string GetWinningTeamName()
{
return WinningTeamName;
}
public string GetMatchFormat()
{
return MatchFormat;
}
public int GetTeamOneOdds()
{
return TeamOneOdds;
}
public int GetTeamTwoOdds()
{
return TeamTwoOdds;
}
}
Klasa korzystająca z dwóch poprzednich, w której przydałoby się właśnie zsynchronizować działanie:
public class DatabaseFiller
{
private MatchesContext Context = null;
private CSGOMatch CurrentMatch = null;
private DivPuller divPuller = null;
private DataParser dataParser = null;
private int MatchId;
private string WebAddress = "http://csgolounge.com/match?m=";
private int BeginFilling = 0;
private int EndFilling = 0;
public DatabaseFiller(int begin, int end)
{
BeginFilling = begin;
EndFilling = end;
Context=new MatchesContext();
for (BeginFilling = begin; BeginFilling < EndFilling; BeginFilling++)
{
MatchId = BeginFilling;
string correctwebaddress = WebAddress + MatchId;
divPuller = new DivPuller(WebAddress + MatchId.ToString());
Task taskA = Task.Factory.StartNew(() => divPuller.ExtractStringFromDiv());
Task<string> getRequiredString = divPuller.ExtractStringFromDiv();
taskA.Wait(10000);
if (taskA.IsCompleted)
{
MessageBox.Show(divPuller.GetResult());
dataParser = new DataParser(divPuller.GetResult()); // bez wczesniejszgo MessageBoxa tutaj dostaję nulla
//dataParser = new DataParser(content);
CurrentMatch = new CSGOMatch()
{
TeamOneName = dataParser.GetTeamOneName(),
TeamTwoName = dataParser.GetTeamTwoName(),
MatchFormat = dataParser.GetMatchFormat(),
TeamOneOdds = dataParser.GetTeamOneOdds(),
TeamTwoOdds = dataParser.GetTeamTwoOdds(),
WinningTeamName = dataParser.GetWinningTeamName(),
};
Context.CsgoMatches.Add(CurrentMatch);
Context.SaveChanges();
}
else
{
MessageBox.Show("Timed out");
}
}
}
}
Problem polega na tym, że ta metoda:
divPuller.ExtractStringFromDiv();
chwilę trwa i w efekcie dostaję nulla jako argument dla:
dataParser = new DataParser(divPuller.GetResult());
Wstawienie MessageBoxa daje czas na pobranie i obróbkę danych, no ale tak to to chyba nie powinno wyglądać. Jak więc "nakazać" programowi oczekiwanie na wykonanie się metody:
divPuller.ExtractStringFromDiv();
i dopiero przejsćie do dalszego wykonywania się?