Latest Publications
Daca nu vine Mahomed la munte, vine muntele la Mahomed
Cine a scornit zicala asta sunt sigur ca nu a mai plecat in Padis (sau oriunde-vrea-muschii-tai) pana la urma, motiv pentru care cel mai bun lucru este sa organizez eu.
Singura regula este:
Merg si daca e sa merg de unul singur.
Pana aici a fost foarte usor, urmeaza sa pregatesc echipamentul.
Revin…
Asp.net Treeview cu multicoloane
Controlul treeview din toolbox nu stie sa afiseze nodurile pe mai multe coloane.
Acest lucru poate fi facut cu CSS in felul urmator, cu conditia sa stabilim dinainte un separator de coloana (in exemplul nostru acesta este caracterul spatiu):
protected void TreeView1_TreeNodeExpanded(object sender, TreeNodeEventArgs e)
{
string[] columns = e.Node.Text.Split(' ');
e.Node.Text = string.Format("<div style='width: 100px; float: left; color: red; background-color: #ddd;'>{0}</div><div style='width: 100px; float: left;'>{1}</div><div style='clear:both;'></div>", columns[0], columns[1]);
}
Exemplul de mai sus este pentru 2 coloane, dar nu ne opreste nimeni sa folosim foreach pentru un numar nelimitat de coloane.
Cum am implementat serviciul web asp.net pentru infovalutar.ro
La sugestia lui Andrei Ignat, am decis sa scriu un tutorial cu modul in care am folosit serviciul web pentru cursul valutar, serviciu web expus prin infovalutar.ro.
Am facut un tutorial despre cum am implementat serviciul web (expus prin infovalutar.ro) pentru site-ul de stiri www.portalsm.ro.
O modalitate imediata de a folosi un serviciu web ar fi asa:
ro.infovalutar.www.Curs curs = new ro.infovalutar.www.Curs();
try
{
Response.Write(string.Format(" 1 EUR {0}<br />", curs.GetLatestValue("EUR")));
Response.Write(string.Format(" 1 USD {0}<br />", curs.GetLatestValue("USD")));
Response.Write(string.Format("100 HUF {0}<br />", curs.GetLatestValue("HUF") * 100));
Response.Write(string.Format(" 1 XAU {0}<br />", curs.GetLatestValue("XAU")));
}
catch (Exception)
{
Response.Write("Something unexpected happened.");
}
Quick and dirty as putea spune.
Am sa enumar doua cazuri in care aceasta metoda nu corespunde asteptarilor mai mari.
1. Daca serviciul web e indisponibil in momentul apelului, nu avem ce valori sa afisam.
2. Pentru fiecare afisare pagina cere date de la serviciul web. Cu cat pagina este afisata mai frecvent, cu atat overhead-ul va fi mai consistent.
Cazul 1.
Pentru momentele in care pagina nu poate aduce datele de la serviciul web, avem nevoie sa persistam (am preferat sa nu traduc acest termen pentru a nu pierde din sens) aceste date in format XML (dar pot fi folosite si alte formate structurate cum e JSON).
Exemplu: fisierul exchange.xml
<?xml version="1.0" encoding="utf-8"?>
<exchange_rate date="13.01.2010">
<currency>
<name>EUR</name>
<unit>1</unit>
<value>4.1215</value>
<percent>-0.34 %</percent>
<difference>-0.0140</difference>
</currency>
<currency>
<name>USD</name>
<unit>1</unit>
<value>2.8388</value>
<percent>-0.43 %</percent>
<difference>-0.0123</difference>
</currency>
</exchange_rate>
Pentru tag-urile unit, name si value nu este necesara nici o explicatie, am sa ma opresc asupra ultimelor doua, percent si difference.
Percent reprezinta evolutia procentuala dintre valoarea curenta si valoarea precedenta a cursului valutar.
Difference reprezinta evolutia diferentiala dintre valoarea curenta si valoarea precedenta a cursului valutar.
Daca pentru valoarea curenta am folosit metoda curs.GetLatestValue(currency), pentru valoarea precedenta am folosit curs.GetValue(currentDate.AddDays(-1), currency), unde currentDate este data curenta intoarsa de sistem (atentie la diferentele de curs orar daca faceti hosting-ul pe servere din alte zone geografice), iar currency este valuta pentru care se doreste cursul.
Pentru cazul in care fisierul exchange.xml nu este creat sau s-a pierdut putem regenera acest fisier cu date generice furnizand aplicatiei un string cu continut XML, dupa cum urmeaza.
string exchange = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
System.Xml.XmlDocument xmlDocument = new System.Xml.XmlDocument();
xmlDocument.LoadXml(exchange);
xmlDocument.Save(AppDomain.CurrentDomain.BaseDirectory + "/exchange.xml");
Pentru ca datele locale sa fie consistente cu cele de la serviciul web, le citesc la un interval de 30 de minute (poate fi ales orice interval dorit) in Global.asax cu o functie apelata pe un thread luat din ThreadPool si regenerez fisierul XML cu date proaspete. Exemplific prin portiunea de cod de mai jos.
void Application_Start(object sender, EventArgs e)
{
object stateExchange = null;
System.Threading.ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(Functions.GetExchange), stateExchange);
}
In acest moment m-am asigurat ca am datele in format XML pentru orice scenariu.
Cazul 2.
In cazul unui portal sau al unui ziar electronic unde pagina pe care avem cursul valutar este ceruta de zeci sau sute de ori pe secunda, este necesara pastrarea acestor date (intreg fisierul XML) in cache, atata timp cat fisierul XML ramane neschimbat. Nu este de dorit ca pagina sa faca citiri din fisierul XML prea des. (aici doresc sa multumesc pentru sugestii unui coleg din RONUA, Andrei Rinea pentru combinatia cu functii callback si caching)
XmlDocument xmlDocument = HttpRuntime.Cache["exchange"] as XmlDocument;
if (xmlDocument == null)
{
xmlDocument = new XmlDocument();
if (File.Exists(Server.MapPath("~/exchange.xml")))
xmlDocument.Load(Server.MapPath("~/exchange.xml"));
else
xmlDocument.LoadXml("");
HttpRuntime.Cache.Insert("exchange", xmlDocument, new CacheDependency(Server.MapPath("~/exchange.xml")), Cache.NoAbsoluteExpiration, Cache.NoSlidingExpiration);
}
Am rezolvat si problema de performanta.
In continuare mai am doua sugestii utile, una legata de functionalitate si cealalta de design.
A) Daca avem un site in care cursul valutar apare in mai multe locuri (pagini), e de preferat sa incapsulam totul intr-un user control (aici ma refer la aducerea datelor din XML si pastrarea lor in cache).
B) In cazul in care cursul valutar urmeaza sa fie folosit pe pagini diferite din punct de vedere al design-ului, poate chiar pe site-uri diferite, putem sa separam partea de design de partea de date folosind XSLT ca in exemplul de mai jos.
MemoryStream stream = new MemoryStream();
XmlDocument XmlDoc = new XmlDocument();
XmlDoc.Load(Server.MapPath("~/exchange.xml"));
XslCompiledTransform XslDoc = new XslCompiledTransform();
XslDoc.Load(Server.MapPath("~/exchange.xslt"));
XslDoc.Transform(XmlDoc, null, stream);
stream.Seek(0, SeekOrigin.Begin);
StreamReader reader = new StreamReader(stream);
exchange = reader.ReadToEnd();
Urmeaza fisierul exchange.xslt.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:template match="*">
<div>
<h2>Curs valutar: <xsl:value-of select = "@date" /></h2>
</div>
<div>
<table cellpadding="1px" cellspacing="0" style="font-size:8pt; margin: 0 auto; padding: 5px 0; text-align:right;">
<xsl:apply-templates />
</table>
</div>
</xsl:template>
<xsl:template match="currency">
<tr>
<td><xsl:value-of select = "unit" /></td>
<td><xsl:value-of select = "name" /></td>
<td><xsl:value-of select = "value" /></td>
<td>
<xsl:choose>
<xsl:when test = "difference > 0">
<img src='http://www.portalsm.ro/Satu-Mare/Images/up-arrow.png' alt='' style='vertical-align: -2px;' />
</xsl:when>
<xsl:when test = "difference < 0">
<img src='http://www.portalsm.ro/Satu-Mare/Images/down-arrow.png' alt='' style='vertical-align: -2px;' />
</xsl:when>
<xsl:when test = "difference = 0">
<img src='http://www.portalsm.ro/Satu-Mare/Images/stop.gif' alt='' style='vertical-align: -2px;' />
</xsl:when>
</xsl:choose>
</td>
<td><xsl:value-of select = "difference" />,</td>
<td><xsl:value-of select = "percent" /></td>
</tr>
</xsl:template>
</xsl:stylesheet>
E in pregatire codul sursa pentru cei care sunt interesati.