De cite ori nu v-a enervat AddReference ?
Pentru VS2010 au reusit sa faca ceva bun :
http://blogs.msdn.com/b/lisa/archive/2010/06/08/visual-studio-2010-pro-power-tools-released-today.aspx
si inca ceva : Colorized Parameter Help - coloreaza help-ul asa cum e colorat codul
Download aici (free)
http://visualstudiogallery.msdn.microsoft.com/en-us/d0d33361-18e2-46c0-8ff2-4adea1e34fef
Cum fac simplu citirea de pagini de pe Web ? Prima varianta este sa incercati cu WebRequest , http://msdn.microsoft.com/en-us/library/system.net.webrequest.aspx . Dar ar trebui sa parsati HTML-ul care NU este XHTML … si imediat va ginditi ca mai sunt sute altii care au aceeasi problema. Asa ca am gasit HTML Agility Pack , http://www.codeplex.com/htmlagilitypack , care stie sa transforme un HTML in XHTML.
Codul pentru incarcarea unei pagini e ridicol de simplu :
HtmlWeb hw = new HtmlWeb(); hw.AutoDetectEncoding = true; HtmlDocument doc = hw.Load(Url); HtmlNode NodeRoot = doc.DocumentNode;
Si de la NodeRoot puteti incepe XPATH cu SelectNodes
Am avut ocazia sa am pe mina Dot Trace de la Jet Brains. Si l-am incercat pe o aplicatie Windows, dar unde si-a aratat puterea a fost in aplicatii Web.
Ca sa vezi ce iti ia cel mai mult timp din aplicatie ai putea sa faci asa : dupa ce ai facut un snapshot, apesi pe plain view, dupa care apesi pe filter. Frumos din partea lui ca vine cu citeva definitii deja existente pentru tool-uri pe care nu vrei sa le vezi la inceput :
Dar poti sa mai adaugi si tu altele – daca vezi ca obtii in fata alte chestii(MS, log4net) de care vrei sa scapi si esti 99% sigur ca sunt optimizate la greu.
In fine, dupa ce dai OK, poti incepe sa inspectezi codul. Asa am vazut ca, pentru o pagina in care ar fi trebuit chemat “get_LastStep” de 9 ori , se chema de 18 ori cu un timp total de 11 ms. Concluzia : se impune un cache – macar primitiv!
Ca puncte tari : are export in XML. Dar tare m-ar fi bucurat un export in Excel – sa pot sa fac eu sortare /filtrare si alte chestii…
De avut NEAPARAT!
De folosit oricind dupa terminarea unui proiect(atentie : aici se vede cit de bune au fost testele!)
Pentru infovalutar (mai exact, pentru mine …) am vrut sa preiau licitatiile de la banci.
Pentru rezultatul final, vezi http://infovalutar.ro/licitatie
Dar sa vedem care a fost povestea :am inceput cu preluarea paginilor HTML . Incepusem cu HttpWebRequest - dar am descoperit la timp HtmlAgilityPack si am ramas credincios lui.
Acum, dupa preluarea paginilor HTML( de ex., http://www.banca-romaneasca.ro/main.php?did=527&code=executare+silita) a fost de ajuns un XPath + expresie regulata de parsare a text-ului din interior.
Ce mi-a produs batai de cap a fost http://vanzari.leumi.ro/bunuri_imobile.html – aveau bunurile in document Word! Ori, ca sa ii ceri celui de la Hosting sa instaleze Word-ul ca sa il instantiezi tu in ASP.NET e aproape imposibil!
Solutia : ASPOSE.WORDS - citeste documente dintre cele mai diverse si scoate un TXT superb – si asta, fara sa aiba nevoie de WORD instalat(se prea poate sa fi omorit muste cu tunul …)
Ca folosire, trebuia sa ii dau un Stream – dar cind am incercat sa ii dau stream-ul de document, mi-a zis ca nu suporta Seek. Asa incit am rezolvat cu un MemoryStream :
public string LeumiData(string URL) { byte[] buffer = new byte[1024*1024*4];
HttpWebRequest hwr = WebRequest.Create(URL) as HttpWebRequest; using (WebResponse response = hwr.GetResponse()) { using (Stream responseStream = response.GetResponseStream()) { using (MemoryStream memoryStream = new MemoryStream()) { int count = 0; do { count = responseStream.Read(buffer, 0, buffer.Length); memoryStream.Write(buffer, 0, count);
} while (count != 0);
//ASPOSE Document d = new Document(memoryStream); return d.ToTxt();
} } }
}
Pot sa spun ca ASPOSE, daca vreti manipulare de documente, face toti banii!
La ce foloseste StructureMap ? Raspunsul pe scurt : este un tool simplu de DI
Raspunsul pe lung : in postul precedent, vroiam sa testez un export de fisiere fara sa ating BD.
Initial, codul arata cam asa :
public ActionResult ExportDate(string id) {
DateExport fe = new DateExport(); fe.id = id;
export exp = new export(Server.MapPath("~/bin/Templates"));
FileContentResult fcr = new FileContentResult(exp.Export(fe), "application/ms-word"); fcr.FileDownloadName = fe.Number + ".doc";
return fcr;
unde variabila fe din
DateExport fe = new DateExport();
atingea BD in momentul in care cineva ii cerea niste date.
Vroiam sa o inlocuiesc cu o variabila ce trimite null ca date si apoi cu variabila care trimite niste date fake.
Ca sa fac acest lucru, am extras metodele care ma interesau din DateExport, am facut o interfata din ele IDateExport, am inlocuit vparametrul din functia export ce cerea un DateExport cu IDateExport si am folosit StructureMap :
In global.asax am definit cererea default :
ObjectFactory.Initialize(x => { x.ForRequestedType<IDateExport>().TheDefault.Is.ConstructedBy(() => new DateExport(HttpContext.Current.User.Identity.Name)); });
si apoi in metoda am inlocuit new cu ObjectFactory :
IDateExport fe = ObjectFactory.GetInstance<IDateExport>(); fe.id = id;
Acum testul automat a devenit floare la ureche – sa zicem ca vreau ca metodele din IDataExport sa nu aduca nimic –si sa testez acest lucru . Creez o clasa DataExportNotFind, implementez IDataExport ca sa nu aduca nimic si scriu in test :
ObjectFactory.Initialize(x => { x.ForRequestedType<IDataExport>().TheDefaultIsConcreteType<DataExportNotFind>(); });
Si asta e tot!
As vrea sa pun accent pe faptul ca , desi testez rapid pe masina proprie facind un fake la BD, totusi, la integrare, ar trebui sa aveti teste cu BLL / UI care sa testeze NEAPARAT cu BD …
Destul de tirziu mi-am dat seama de beneficiile aduse de un Mock – dar mai bine mai tirziu decit niciodata …
Ma gindeam ca niciodata nu o sa il folosesc – ca ajunge sa verific BLL cu unit test(NUNIT/VS Test) , site-ul Web cu NUnit ASP/WATI(N|R) / Selenium , Windows Forms cu NUnitForms si nu o sa am nevoie de Mock.
Adevarul este ca da, nu as avea nevoie de Mock … decit daca as vrea sa verific mai repede unele date, fara sa ating BD.De exemplu, pot sa verific controller-ele fara sa am nevoie sa instantiez HttpContext si BD. Sa zicem ca am un controller care are o actiune ce doar exporta un fisier Word– bazat pe un template. Codul din fisier arata cam asa :
Daca as vrea sa verific rapid metoda aceasta ar trebui sa nu ating baza de date si sa am pun un rezultat in loc de Server.MapPath ?
Se vede clar ca deja folosesc StructureMap, deci nu ar fi o problema cu gasitul a niste date fake. Dar pentru Server.MapPath intervine stralucit Mock.
Am preluat de la Hanselmann MVC Mock Helpers ,iar codul de test arata cam asa :
exportController i = new exportController(); MockRepository mocks = new MockRepository(); using (mocks.Record()) { //MvcMockHelpers.SetFakeControllerContext(mocks, i); mocks.SetFakeControllerContext(i); SetupResult.For(i.ControllerContext.HttpContext.Server.MapPath(null)).IgnoreArguments().Return(@"c:\programs\templates"); // cod pentru chemarea Server.MapPath mocks.ReplayAll(); } using (mocks.Playback()) { ObjectFactory.Initialize(x => { x.ForRequestedType<IExport>().TheDefaultIsConcreteType<FactFind>();// FactFind nu atinge BD }); FileContentResult fcr = i.exportdate("865", "A") as FileContentResult; fcr.ShouldNotBeNull(); fcr.FileContents.Length.ShouldBeGreaterThan(0);//TODO : Verifica si continutul }
Aveam nevoie de un tracker de issue – pentru ca incepusem sa am prea multe cereri de modificare si nu ma mai descurcam. De fapt, la o a doua vedere, nu erau atit de multe cereri, cit detalii ale lor.
Am cerut pareri pe RONUA – dar ceea ce mi s-a oferit de acolo era pe bani (ma rog, OnTime se pare ca are oferte speciale..). Iar in criza actuala sa cer bani de la servici ca eu nu ma mai descurc suna cam prost…
Dupa ce am citit de mai multe , m-am decis pentru BugTracker.NET. Am avut doua probleme : Integrarea cu Active Directory – din fericire Web.Config este destul de clar scris – si trimiterea emailurilor. Nu vroiam sa configurez SMTP – ci doar sa isi ia setarile din Web.Config de forma:
<system.net> <mailSettings> <smtp> <network host="xxxx" port="25"/> </smtp> </mailSettings> </system.net>
Din nefericire el lucra cu System.Web.Mail – asta pentru ca ,zicea el, noul System.NET.Mail nu suporta SSL.
In fine – nu aveam nevoie de SSL –asa ca am muncit sa il transform – si am modificat pe ici, pe colo, prin partile esentiale email.cs:
/* Copyright 2002-2008 Corey Trager Distributed under the terms of the GNU General Public License */
using System; using System.Collections; using System.IO; using System.Text;
// disable System.Net.Mail warnings #pragma warning disable 618
namespace btnet {
public class Email { /////////////////////////////////////////////////////////////////////// public static string send_email( // 5 args string to, string from, string cc, string subject, string body) { return send_email( to, from, cc, subject, body, false, System.Net.Mail.MailPriority.Normal, null, false); }
/////////////////////////////////////////////////////////////////////// public static string send_email( // 6 args string to, string from, string cc, string subject, string body, bool isbodyhtml) { return send_email( to, from, cc, subject, body, isbodyhtml, System.Net.Mail.MailPriority.Normal, null, false); }
/////////////////////////////////////////////////////////////////////// public static string send_email( string to, string from, string cc, string subject, string body, bool isbodyhtml, System.Net.Mail.MailPriority priority, int[] attachment_bpids, bool return_receipt) { ArrayList files_to_delete = new ArrayList(); ArrayList directories_to_delete = new ArrayList(); System.Net.Mail.MailMessage msg = new System.Net.Mail.MailMessage(from, to); if (!string.IsNullOrEmpty(cc.Trim())) { msg.CC.Add(cc); } msg.Subject = subject; msg.Priority = priority;
// This fixes a bug for a couple people, but make it configurable, just in case. if (Util.get_setting("BodyEncodingUTF8", "1") == "1") { msg.BodyEncoding = Encoding.UTF8; }
if (return_receipt) { msg.Headers.Add("Disposition-Notification-To", from); }
// workaround for a bug I don't understand... if (Util.get_setting("SmtpForceReplaceOfBareLineFeeds", "0") == "1") { body = body.Replace("\n", "\r\n"); }
msg.Body = body; msg.IsBodyHtml = isbodyhtml;
if (attachment_bpids != null && attachment_bpids.Length > 0) {
string upload_folder = btnet.Util.get_upload_folder();
if (string.IsNullOrEmpty(upload_folder)) { upload_folder = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); Directory.CreateDirectory(upload_folder); directories_to_delete.Add(upload_folder); }
foreach (int attachment_bpid in attachment_bpids) { byte[] buffer = new byte[16 * 1024]; string dest_path_and_filename; Bug.BugPostAttachment bpa = Bug.get_bug_post_attachment(attachment_bpid); using (bpa.content) { dest_path_and_filename = Path.Combine(upload_folder, bpa.file); using (FileStream out_stream = new FileStream( dest_path_and_filename, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { int bytes_read = bpa.content.Read(buffer, 0, buffer.Length); while (bytes_read != 0) { out_stream.Write(buffer, 0, bytes_read);
bytes_read = bpa.content.Read(buffer, 0, buffer.Length); } }
System.Net.Mail.Attachment mail_attachment = new System.Net.Mail.Attachment( dest_path_and_filename); msg.Attachments.Add(mail_attachment); files_to_delete.Add(dest_path_and_filename); } }
try { // This fixes a bug for some people. Not sure how it happens.... msg.Body = msg.Body.Replace(Convert.ToChar(0), ' ').Trim(); System.Net.Mail.SmtpClient s=new System.Net.Mail.SmtpClient(); s.Send(msg);
// We delete late here because testing showed that SmtpMail class // got confused when we deleted too soon. if (files_to_delete.Count > 0) { foreach (string file in files_to_delete) { File.Delete(file); } }
if (directories_to_delete.Count > 0) { foreach (string directory in directories_to_delete) { Directory.Delete(directory); } }
return ""; } catch (Exception e) { Util.write_to_log("There was a problem sending email. Check settings in Web.config."); Util.write_to_log("TO:" + to); Util.write_to_log("FROM:" + from); Util.write_to_log("SUBJECT:" + subject); Util.write_to_log(e.GetBaseException().Message.ToString()); return (e.GetBaseException().Message); }
} // end Email
} // end namespace
Auzisem de NCover – dar nu avusesem posibilitatea sa il pun la lucru.
De ce vroiam sa il folosesc ? Simplu: aveam teste automate (NUNIT) pentru proiect –unele cu Baza de date, altele fara. Ceea ce vroiam sa aflu este cit de mult acopar din codul sursa – adica cit de mult testele sunt complete.
OK – citind putin a inceput sa devina evident cum sa il folosesc – desi are o curba initiala de invatat de vreo 5 minute(nu e evident)
Primul lucru, daca vreti sa il folositi cu NUnit, este sa incercati sa rulati proiectul NUnit din consola, ceva de genul
nunit-console “<fisier.nunit>"
( daca vreti, puteti continua linia cu /include=categorii_de_teste )
Daca a rulat OK ( adica fara erori… ) acum putem configura NCover . Porniti NCover Explorer, apasati CTRL+N si o sa vi se deschida o fereastra de comanda in care veti introduce calea catre nunit-console iar la application arguments calea catre fisierul NUnit.
Rulati si o sa aveti rapid o evidenta in ce proportie codul din clase s-a executat. In plus va puteti uita direct intr-o metoda sa vedeti care cod s-a executat si care nu ( theme Underline mi se pare cea mai buna)
Generarea rapoartelor e buna – doar ca nu lanseaza fisierul htm generat si trebuie sa il gasiti singuri ( de aceea are “Explore coverage folder”)
Un tool exceptional, ce il puteti rula si din command line . Recomandat cu tarie!
Ce este NBuilder ? Un generator automat de obiecte – la care adauga proprietati default.
De ex:
var generator = new UniqueRandomGenerator(); var seq = new SequentialGenerator<int> { Direction = GeneratorDirection.Ascending, Increment = 1}; seq.StartingWith(1); BuilderSetup.DisablePropertyNamingFor<Linii, int>(x => x.Pret);
Factura oh = Builder<Factura>.CreateNew().Build(); var q = Builder<Linii>.CreateListOfSize(10) .WhereAll() .Have(x=>x.Qty=generator.Next(1,100)) .And(x=>x.LineNumber = seq.Generate()) .Build();
oh.Linii.AddRange(q);
E foarte bun la asta – si l-am folosit atunci cind vroiam generarea de date discrete ca sa le export in Excel…
Ce am invatat de la NBuilder :
E free – si f bun la generare. Diferenta de timp intre el si baza de date este de 1088 ms versus 3141 ms. Folositi cu incredere la teste!
Pentru usurinta in folosire,Smart Assembly capata nota 10.
Observatii :
<UsingTask TaskName="SmartAssembly.MSBuild.Tasks.Build" AssemblyName="SmartAssembly.MSBuild.Tasks,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=cd3409ee69028647" /> <Target Name="AfterBuild"> <SmartAssembly.MSBuild.Tasks.Build ProjectFile="proiect.{sa}proj" OverwriteAssembly="true" MarkAsReleased="true"/>
</Target>
Atentie – nu e free…Dar e bun – si are o groaza de optiuni.
Download http://www.smartassembly.com/
Daca aveti nevoie sa parsati o pagina Web va sfatuiesc sa folositi Html Agility Pack . Stie sa parseze pagini Web si sa le transforme in xml valid (ma rog, pentru cei incepatori , atentie ,de ex, la img : se inchide fara innertext)
Cum l-am folosit: Pai, ca sa parsez cursurile valutare de pe paginile bancilor.
Codul a fost extrem de simplu :
HtmlWeb hw = new HtmlWeb();
HtmlDocument doc = hw.Load(Url);
NodeRoot= doc.DocumentNode;
HtmlNodeCollection valori = NodeRoot.SelectNodes(“//td[@class='td-body text-black align-left' and @style='width:auto']”);
si apoi iterati in valori si obtineti ce vreti. Cel mai greu este sa gasiti XPATH-ul corect – dar asta revine la XML, nu la HTML.
De ce sa il folositi? Cite pagini VALIDE XHTML cunoasteti ?
Programe, majoritatea(99,9%) free , pe care le folosesc ca programator si ca pasionat al Web-ului si GTD
E o continuare la http://serviciipeweb.ro/iafblog/2008/01/18/mini+Free+Tools+List+2007.aspx
si bineinteles inspirata de Hanselman
Fara prea multa discutie, iata lista:
Programare
Addonuri free pentru programe pe bani
USB
Care sunt ale voastre ?
Theme design by Jelle Druyts
Pick a theme: BlogXP business calmBlue Candid Blue dasBlog dasblogger DirectionalRedux Discreet Blog Blue Elegante essence Just Html Mono Movable Radio Blue Movable Radio Heat orangeCream Portal Project84 Project84Grass Slate Tricoleur