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:
01 | ro.infovalutar.www.Curs curs = new ro.infovalutar.www.Curs(); |
05 | Response.Write( string .Format( " 1 EUR {0}<br />" , curs.GetLatestValue( "EUR" ))); |
06 | Response.Write( string .Format( " 1 USD {0}<br />" , curs.GetLatestValue( "USD" ))); |
07 | Response.Write( string .Format( "100 HUF {0}<br />" , curs.GetLatestValue( "HUF" ) * 100)); |
08 | Response.Write( string .Format( " 1 XAU {0}<br />" , curs.GetLatestValue( "XAU" ))); |
12 | 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
01 | <? xml version = "1.0" encoding = "utf-8" ?> |
02 | < exchange_rate date = "13.01.2010" > |
07 | < percent >-0.34 %</ percent > |
08 | < difference >-0.0140</ difference > |
14 | < percent >-0.43 %</ percent > |
15 | < difference >-0.0123</ difference > |
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.
1 | string exchange = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" ; |
3 | System.Xml.XmlDocument xmlDocument = new System.Xml.XmlDocument(); |
5 | xmlDocument.LoadXml(exchange); |
7 | 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.
1 | void Application_Start( object sender, EventArgs e) |
3 | object stateExchange = null ; |
5 | 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)
01 | XmlDocument xmlDocument = HttpRuntime.Cache[ "exchange" ] as XmlDocument; |
03 | if (xmlDocument == null ) |
05 | xmlDocument = new XmlDocument(); |
07 | if (File.Exists(Server.MapPath( "~/exchange.xml" ))) |
08 | xmlDocument.Load(Server.MapPath( "~/exchange.xml" )); |
10 | xmlDocument.LoadXml( "" ); |
12 | 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.
1 | MemoryStream stream = new MemoryStream(); |
2 | XmlDocument XmlDoc = new XmlDocument(); |
3 | XmlDoc.Load(Server.MapPath( "~/exchange.xml" )); |
4 | XslCompiledTransform XslDoc = new XslCompiledTransform(); |
5 | XslDoc.Load(Server.MapPath( "~/exchange.xslt" )); |
6 | XslDoc.Transform(XmlDoc, null , stream); |
7 | stream.Seek(0, SeekOrigin.Begin); |
8 | StreamReader reader = new StreamReader(stream); |
9 | exchange = reader.ReadToEnd(); |
Urmeaza fisierul exchange.xslt.
01 | <? xml version = "1.0" encoding = "utf-8" ?> |
03 | < xsl:template match = "*" > |
05 | < h2 >Curs valutar: < xsl:value-of select = "@date" /></ h2 > |
08 | < table cellpadding = "1px" cellspacing = "0" style = "font-size:8pt; margin: 0 auto; padding: 5px 0; text-align:right;" > |
09 | < xsl:apply-templates /> |
14 | < xsl:template match = "currency" > |
16 | < td >< xsl:value-of select = "unit" /></ td > |
17 | < td >< xsl:value-of select = "name" /></ td > |
18 | < td >< xsl:value-of select = "value" /></ td > |
21 | < xsl:when test = "difference > 0" > |
24 | < xsl:when test = "difference < 0" > |
27 | < xsl:when test = "difference = 0" > |
32 | < td >< xsl:value-of select = "difference" />,</ td > |
33 | < td >< xsl:value-of select = "percent" /></ td > |
E in pregatire codul sursa pentru cei care sunt interesati.