De ce taskuri asincrone ? In ideea ca , intr-o aplicatie
Windows(si chiar ASP.NET) , operatiile lungi ar trebui sa fie executate de
catre alt thread, urmind ca aplicatia sa poata sa mai afiseze ceva
utilizatorulu in tot acest timp ( fie si un buton pe care scrie „apasa ca sa
intrerupi operatia asta lunga …”). De pilda, in aplicatia noastra, daca avem
mai mult de 100 de Publisher-i si vrem sa ii vedem pe toti – ar trebui
incarcati intr-un nou thread.
Ne ocupam mai intii de o aplicatie Windows Forms si pe urma
de ASP.NET
Un thread nu e greu de pornit. Hai sa vedem un exemplu:
System.Threading.Thread t = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(StartAction));
t.Start(“obiect transmis”);
public void
StartAction(object o)
{
string
s = o.ToString();
System.Threading.Thread.Sleep(5000);
//executa
actiunea
//trimite
text
if
(this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate()
{
this.Text = s;
});
//sau
//this.Invoke(new MethodInvoker(evenimentfaraparametri());
//this.Invoke(new EventHandler(btnDiscounts_Click));
//this.BeginInvoke(new EventHandler(eveniment cu
parametri));
}
else
this.Text = s;
}
Totusi,
exista o problema – dintr-un thread nu se pot accesa DIRECT controale din alt
thread – si de aceea avem instructiunea this. Invoke .Diferenta intre this.Invoke si este this.BeginInvoke aceea ca prima instructiune asteapta rezultatul
actiunii, pe cind a doua doar executa si se intoarce imediat sa execute codul
ramas.
De
aceea exista controlul numit BackgroundWorker – care asigura ca , din
evenimentul propriu generat, sa accesezi orice obiect de pe forma. O sa facem
acest lucru pentru salvarea in XML a colectiei de Publisher-i in format XML.
.NET
are o forma usoara de a salva o colectie/clasa in format XML , salvindu-i
proprietatile.
Vom
utiliza modalitatea cea mai usoara de a
face acest lucru
Marcam
clasa Publisher si clasa colectie ColPublisher cu atributul de [Serializable]
:
[Serializable]
public
class ColPublisher
: System.Collections.ObjectModel.KeyedCollection<string,Publisher>
[Serializable]
public
class Publisher
Acum
o sa facem serializarea obiectului Publisher:
#region Serializer
/// <summary>
/// instanta pentru lazy load
/// </summary>
private
static XmlSerializer m_Serializer;
/// <summary>
/// serializator pentru obiectul publisher
/// </summary>
private
static XmlSerializer Serializer
{
get
{
if (m_Serializer == null)
m_Serializer = new XmlSerializer(typeof(Publisher));
return m_Serializer;
}
}
/// <summary>
/// salveaza obiectul ca XML
/// </summary>
[XmlIgnore]
public
string XML
{
get
{
StringBuilder sb = new
StringBuilder();
EncodingStringWriter sw = new
EncodingStringWriter(sb, Encoding.Default);
XmlTextWriter xtw = new XmlTextWriter(sw);
Serializer.Serialize(xtw, this);
return sb.ToString();
}
}
/// <summary>
/// recreeeaza un Publisher dintr-un string XML
/// </summary>
/// <param
name=”XML”>string care contine
tot </param>
/// <returns></returns>
public
static Publisher
FromXML(string XML)
{
StringReader sr = new StringReader(XML);
return
Serializer.Deserialize(sr) as Publisher;
}
#endregion
Copiem
apoi acelasi cod( cu citeva diferente) si pentru ColPublisher
Citeva
comentarii despre cod:
De ce am pus [XmlIgnore] peste public string XML ? Pentru a nu serializa
si aceasta proprietate, dind astfel nastere la o nedorita recursivitate
Ce e cu clasa EncodingStringWriter
? Este facuta pentru a putea schimba Encoding=ul- daca aveti de exemplu
caractere speciale(diacritice) romanesti/franceze/etc.
De ce metoda FromXML este statica- iar XML este pe
instanta? Asa mi se pare normal – transformarea dintr-un obiect in XML sa
apartina obiectului, iar din XML in obiect nu poate sa apartina unui obiect(
ah, daca as fi putut scrie this = Serializer.Deserialize(sr) as Publisher !) – ci doar clasei.
Nu se poate face codul mai „generic”? Ba da- una din
deosebiri ar fi ca FromXML ar trebui sa fie pe instanta…
Haideti acum in proiectul Windows sa serializam o colectie
de Publisher-i.Pe forma frmPublisherList adaugam un buton btnSave, cu textul
Save, dublu click si scriem urmatorul cod:
private void
btnSave_Click(object sender, EventArgs e)
{
BookObjects.ColPublisher col =
colPublisherBindingSource.DataSource as
BookObjects.ColPublisher;
string
strSave = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData);
strSave = Path.Combine(strSave, “pub.xml”);
File.WriteAllText(strSave,
col.XML);
System.Diagnostics.Process.Start(strSave);
}
Rulati
proiectul, adaugati 2 publisher-i si apasati pe save.
Este
clar ca, daca sunt multi publisher-i, procesul poate deveni prea lung si
blocheaza interfata.
Haideti
sa folosim background worker. Il adaugam din toolbox , il redenumim bgSave,
dublu click. Luam codul din btnSave_Click, il adaugam la
si pe urma scriem doar bgSave.RunWorkerAsync();
private void
btnSave_Click(object sender, EventArgs e)
{
bgSave.RunWorkerAsync();
}
private
void bgSave_DoWork(object
sender, DoWorkEventArgs e)
{
BookObjects.ColPublisher col =
colPublisherBindingSource.DataSource as
BookObjects.ColPublisher;
string
strSave = System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData);
strSave = Path.Combine(strSave, “pub.xml”);
File.WriteAllText(strSave,
col.XML);
System.Diagnostics.Process.Start(strSave);
}
Data
viitoare o sa vorbim despre modelul
asincron din ASP.NET.