NBuilder

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  :

  1. Ca nu am nevoie neaparat de mocking Stubs
  2. Ca nu ar trebui ca proprietatile sa genereze efecte secundare( ca de exemplu calcularea totalului unei facturi nu ar trebui sa faca refresh la linii, chiar daca nu s-a facut incarcarea liniilor)

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!

Smart Assembly

Pentru usurinta in folosire,Smart Assembly capata nota 10.

Observatii :

  1. isi pastreaza in C:\program data cum a facut modificarile
  2. Daca aveti un site ASP.NET
    • nu bifati “I want to automatically seal classes whenever possible” – nu o sa isi dea seama pentru, de ex,global.asax “ASP.global_asax’: cannot derive from sealed type ‘InfoValNew.MvcApplication”
    • Fiecare dll ar trebui obguscat separat – adica facut un fisier .{sa}proj pentru el .Nu obfuscheaza metodele publice.
    • Folositi WebDeployment projects si in compilati si restul – dind “open project file” si introducind:

      <UsingTask TaskName="SmartAssembly.MSBuild.Tasks.Build" AssemblyName="SmartAssembly.MSBuild.Tasks,&#xD;&#xA;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>

  3. Linq trebuie sa stea intr-un proiect al lui, separat…  sau sa excludeti clasele linq de la compilare.
  4. Cum e si normal, daca il includeti in build, merge mai incet compilarea – asa incit puneti-l doar pe release …
  5. Verificati daca proiectul merge …

Atentie – nu e free…Dar e bun – si are o groaza de optiuni.

Download http://www.smartassembly.com/

PostSharp si Log4PostSharp

Cred ca toata lumea stie la ce e bun Log4Net – este un logger . E foarte bun la log-at mesaje – in principiu cele de eroare – si trimis in cele mai diverse surse – la de fisiere text la console si la BD.

Dar problema este ca as fi vrut ceva sa imi logheze intrarea si iesirea dintr-o metoda, ceva de genul :

public string Arata(Point P, string s){

logger.Debug(“ am intrat cu Point “ + P + “ si string “ + s);

//code

logger.Debug(“ am iesit cu rezultatul “ + ….)
}

La recomandarea lui Dan Bunea, am dat de PostSharp si Log4PostSharp

Dupa ce m-am chinuit sa il fac sa lucreze , iata la ce am ajuns:

1. Un folder lib , in care am pus fisierele :

lib\a.txt
lib\Default.psproj
lib\Log4PostSharp.dll
lib\Log4PostSharp.pdb
lib\Log4PostSharp.psplugin
lib\Log4PostSharp.Weaver.dll
lib\plugins
lib\PostSharp-1.0.targets
lib\PostSharp-1.0.version
lib\PostSharp-AppDomain.config
lib\PostSharp-Library.config
lib\PostSharp-Platform.config
lib\PostSharp.Core.dll
lib\PostSharp.Core.XmlSerializers.dll
lib\PostSharp.exe
lib\PostSharp.exe.config
lib\PostSharp.Laos.dll
lib\PostSharp.Laos.psplugin
lib\PostSharp.Laos.Weaver.dll
lib\PostSharp.MSBuild.dll
lib\PostSharp.Public.dll
lib\PostSharp.targets
lib\plugins\Log4PostSharp.dll
lib\plugins\Log4PostSharp.pdb
lib\plugins\Log4PostSharp.psplugin

2. Project Properties=> Reference Paths=> adaugat folderul lib

3. Un fisier .psproj numit la fel ca proiectul, in care am scris standard

<?xml version="1.0" encoding="utf-8" ?>
<Project xmlns="http://schemas.postsharp.org/1.0/configuration">
    <SearchPath Directory="bin/{$Configuration}"/>
    <SearchPath Directory="{$SearchPath}" />
    <SearchPath Directory="lib" />   
    <Tasks>
        <AutoDetect />
        <Compile TargetFile="{$Output}" IntermediateDirectory="{$IntermediateDirectory}"  CleanIntermediate="false" />
    </Tasks>
</Project>

4. In csproj adaugat cu mina, linga <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

<PropertyGroup>
   <DontImportPostSharp>True</DontImportPostSharp>
   <PostSharpDirectory>lib\</PostSharpDirectory>
   <PostSharpUseCommandLine>True</PostSharpUseCommandLine>
</PropertyGroup>

<Import Project="$(PostSharpDirectory)PostSharp.targets" Condition=" Exists(‘$(PostSharpDirectory)PostSharp.targets’) " />

5. In AssemblyInfo.cs adaugat  (pentru a genera pentru fiecare metoda)

[assembly: Log(AttributeTargetTypes = "*", EntryLevel = LogLevel.Debug,
    EntryText = "Entering method: {signature} with parameters {paramvalues}", ExitText = "exit method {signature} with {returnvalue}",
  ExitLevel = LogLevel.Debug, ExceptionLevel = LogLevel.Error, AttributePriority = 1)]

Asta ca sa genereze un log pentru fiecare metoda …

6. Incepe log4net Adaugat un fisier log4net.config si pus

log4net.Config.XmlConfigurator.ConfigureAndWatch(new System.IO.FileInfo("log4net.config"));

 

GATA! Configurat – si apoi am uitat de el….

Output-ul este ceva de genul :

2009-07-17 22:24:08,541 [TestRunnerThread] DEBUG fisier.cs  Namespace.Clasa [(null)] – Entering method: Void .cctor() with parameters
2009-07-17 22:24:08,729 [TestRunnerThread] DEBUG fisier.cs  Namespace.Clasa [(null)] – exit method Void .cctor() with
2009-07-17 22:24:08,760 [TestRunnerThread] DEBUG fisier.cs  Namespace.Clasa [(null)] – Entering method: Namespace.ClasaList Find(System.String) with parameters "5888"
2009-07-17 22:24:08,760 [TestRunnerThread] DEBUG   Namespace.ClasaList [(null)] – Entering method: Void .ctor() with parameters
2009-07-17 22:24:08,760 [TestRunnerThread] DEBUG   Namespace.ClasaList [(null)] – exit method Void .ctor() with
2009-07-17 22:24:08,760 [TestRunnerThread] DEBUG clasa2.cs  namespace.DataContext [(null)] – Entering method: Void .cctor() with parameters
2009-07-17 22:24:08,760 [TestRunnerThread] DEBUG clasa2.cs  namespace.DataContext [(null)] – exit method Void .cctor() with
2009-07-17 22:24:09,166 [TestRunnerThread] DEBUG clasa2.cs  namespace.DataContext [(null)] – Entering method: Void .ctor() with parameters
2009-07-17 22:24:09,166 [TestRunnerThread] DEBUG clasa2.cs  namespace.DataContext [(null)] – exit method Void .ctor() with
2009-07-17 22:24:09,198 [TestRunnerThread] DEBUG clasa2.cs  namespace.DataContext [(null)] – Entering method: System.Data.Linq.Table`1<Namespace.clasa1> get_clasa3() with parameters
2009-07-17 22:24:09,401 [TestRunnerThread] DEBUG clasa2.cs  namespace.DataContext [(null)] – exit method System.Data.Linq.Table`1<Namespace.clasa1> get_clasa3() with Table(clasa2)
2009-07-17 22:24:11,307 [TestRunnerThread] DEBUG clasa2.cs  namespace.DataContext [(null)] – Entering method: System.Data.Linq.Table`1<amespace.clasa2> get_clasa4() with parameters
2009-07-17 22:24:11,417 [TestRunnerThread] DEBUG clasa2.cs  namespace.DataContext [(null)] – exit method System.Data.Linq.Table`1<amespace.clasa2> get_clasa4() with Table(clasa2)
2009-07-17 22:24:11,495 [TestRunnerThread] DEBUG fisier.cs  Namespace.Clasa [(null)] – exit method Namespace.ClasaList Find(System.String) with Clasa2List : 0

Asp.NET MVC , MVC Contrib grid si total

Am avut de facut o aplicatie cu ASP.NET MVC  – si bineinteles, cu un grid ce lista niste vinzari si cu total.

Asta e ceea ce mi-a pus probleme : totalul … Asa incit, dupa ce am studiat grid-ul de la ASP.NET MVC Contrib , a trebuit sa il extind. Si nu a fost foarte greu. Mai intii , practica -  sintaxa e asemanatoare, doar ca adaug o functie pentru footer :

<% =Html.HtmlGridFooter<Lines>(ViewData.Model.Lines,
        (x) =>
        {
            decimal total = 0;
            foreach (var s in x) { total += s.Total; };   
            return "<tr><td colspan=1 align=right>Total</td><td colspan=1>"+ total.ToString("#.00")+"</td></tr>";
        }

) si de aici incepe grid-ul obisnuit.

Teoria : a trebuit sa extind HtmlTableGridRenderer si sa il atasez de grid …Probabil ca o sa fie nevoie sa am , odata, doi grid renderer?

Cod :

public class HtmlGridFooter<T> : Grid<T>
        where T : class
    {
        public HtmlGridFooter(IEnumerable<T> dataSource, TextWriter writer, ViewContext context) :
            base(dataSource, writer, context)
        {
        }
        public Func<IEnumerable<T>, string> footer
        {
            set
            {
                this.RenderUsing(new FooterHtmlGrid<T>() { actions = base.DataSource, footer = value });
            }
        }

    }
    public class FooterHtmlGrid<T> : HtmlTableGridRenderer<T>
        where T : class
    {
        public IEnumerable<T> actions;
        public Func<IEnumerable<T>,string > footer;
        protected override void RenderGridEnd(bool isEmpty)
        {
            if (footer != null)
            {
                base.RenderText(footer(actions));
            }
            base.RenderGridEnd(isEmpty);
        }
    }

si intr-o clasa statica ar trebui pus:

public static IGrid<T> HtmlGridFooter<T>(this HtmlHelper helper, IEnumerable<T> dataSource, Func<IEnumerable<T>, string> footer)
        where T : class
        {
            HtmlGridFooter<T> g = new HtmlGridFooter<T>(dataSource, helper.ViewContext.HttpContext.Response.Output, helper.ViewContext);
            g.footer = footer;
            return g;
        }

Cum sa faci/editezi un add-on de Firefox

Pas 0: Pornesti de la un addon existent – in downloadezi cu IE si si ii schimbi extensia in zip si deszip  sau il gasesti in C:\Documents and Settings\<user>\Application Data\Mozilla\Firefox\Profiles\<default>\extensions .  Sau downloadezi add-onul meu de aici . Indentificati fisierul install.rdf si gasiti em:id (la mine cursval@infovalutar.ro )

Pas 1: Inchizi toate instantele de Firefox. Rulezi apoi firefox -no-remote -P devaddon. Se deschide o fereastra in care apesi “Create Profile”

 

 Choose User Profile_active

O numesti devaddon si ai grija sa alegi folder-ul unde se pune profilul – eu am ales C:\1\log

Create Profile Wizard_active

Pas 3: va duceti in folder-ul ales si acolo o sa gasiti folder-ul extensions. Faceti un fisier numit  cursval@infovalutar.ro (cum ati gasit la em:id ) si inauntrul lui puneti calea catre locul unde ati facut deszipuirea.

Pas 4. Porniti firefox -no-remote -P devaddon si gata! Aveti add-onul.Verificati ca e corect

daca da, gata – aveti un addon functional.

daca nu, goto pas 5.

Pas 5. Editati fisierele (la mine sample.xul). Goto Pas 4.

Business intelligence(cu forecast pe curs Euro) revine

Nu eram deloc multumit de cum facusem predictia de curs valutar – exportam datele din tabela de SqlServer in Excel, pe aceasta analizam “in the cloud” si apoi generam fisierul html cu un macro de Excel …Naspa rau! Normal ca vroiam sa se generewze automat de la datele din tabela – si apoi sa se verse singra intr-o BD.

Asa incit am luat decizia sa studiez un pic cum se face(rezultate aici) –si iata ideile principale:

1. Nu va fie frica sa va creati sursele de date , cuburile si dimensiunile (folositi pentru aceasta VS, nu SSMS – din pacate NU merge in SSMS !). E acelasi lucru ca si cum ati defini o BD normala – si ati lucra cu ea.Dupa ce creeati, va trebui sa ii faceti “Deploy”c a sa o vedeti si in Analysis Services. O sa trec la nivelul urmator (generare automata a BI din cod) mai tirziu.

2.Chiar daca Baza de date principala isi schimba valorile,ca sa isi schimbe si Analysis Services  trebuie sa ii dati “Process” din nou …Neplacut, dar asta e. In mod programatic din C#:

using(Microsoft.AnalysisServices.Server s=new Microsoft.AnalysisServices.Server())
            {
                s.Name = "localhost";
                s.Connect("localhost");
                s.Refresh();
                using(Database d=s.Databases["analtest1"])
                {
                    d.Refresh();
                    d.Process(ProcessType.ProcessFull);
                }
            }

3. Pentru a obtine valorile forecastate, sintaxa este uimitor de asemanatoare cu cea obisnuita din ADO.NET:

using (AdomdConnection con = new AdomdConnection())
            {
                con.ConnectionString = "Data Source=localhost;Catalog=xxx";
                con.Open();
                using (AdomdCommand cmd = new AdomdCommand())
                {
                    cmd.CommandText = "select PredictTimeSeries(Valoare,25) AS Predictie from [Vw Eur]";
                    cmd.CommandType = CommandType.Text;
                    cmd.Connection = con;
                    using (AdomdDataReader dr = cmd.ExecuteReader())
                    {

                        while (dr.Read())
                        {

Totusi, te poti insela rapid:

4. Ca sa obtin tabelul de predictie, a trebuit sa vad in SSMS rezultatul si asa m-am prins ca dr are un singur rind, si acela fiind tot un AdomdDataReader 

while (dr.Read())
                       {
                           using (AdomdDataReader pred = dr["Predictie"] as AdomdDataReader)
                           {

                               while (pred.Read())
                               {

4. Nu exista o modalitate standard de a obtine un DataTable dintr-un AdomdDataReader   – iar GetSchemaTable nu intoarce rezultatele ca schema. Asa ca am ajuns la modalitatea obisnuita de a genera DataTable :

while (dr.Read())
                       {
                           using (AdomdDataReader pred = dr["Predictie"] as AdomdDataReader)
                           {

                               int fields = pred.FieldCount;
                               DataTable dt = new DataTable("nume");
                               for (int i = 0; i < fields; i++)
                               {
                                   dt.Columns.Add(pred.GetName(i), pred.GetFieldType(i));
                               }
                               while (pred.Read())
                               {
                                   object[] p = new object[fields + 1];
                                   for (int i = 0; i < fields; i++)
                                   {
                                       p[i] = pred[i];
                                   }
                                   dt.Rows.Add(p);
                               }                                
                           }

De ce n-am facut-o extension method la AdomdDataReader    ? Din YAGNI ( Ok, puturosenie …) Daca o sa fie nevoie , (re)fac…

5. E clar ca nu merge rapid cu BI-ul …nici macar cu ceva simplu ca un forecast amarit …Deci,la treaba!

ASP.NET MVC si Validarea Model Binder

Ca in ASP.NET MVC Best Practices( aici, aici, aici, aici si aici ) am pus o metoda in Controller care sa faca legatura automat cu acea clasa ce trebuie salvata:

 

[AcceptVerbs(HttpVerbs.Post)]
        [Authorize()]
        public ActionResult Index([Bind(Exclude = "IDAlert,IDPerson", Prefix = "")]CV_Alerte c)
        {}

Insa am avut o problema subtila : CV_Alerte are un membru Valoare de tipul double care este mapat la un textbox. Ce se intimpla daca trimit xxx ?

La o cautare rapida pe net, gasesc raspunsul : Verifica ModelState.IsValid . OK, si acum cum iau eroarea ? Nu am gasit pe net, asa ca, dupa o investigare rapida a surselor, am gasit metoda:

if (!ModelState.IsValid)
            {
                foreach (var s in ModelState.Keys)
                {
                    if (!ModelState.IsValidField(s))
                    {
                        ModelState.AddModelError(s,"aveti o eroare la setarea " + s + "–" + ModelState[s].Value.AttemptedValue);
                    }
                }
                return View();
            }

Ar fi trebuit sa o faca ei!

Ca referinta la ASP.NET Best Practices:

http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx
http://weblogs.asp.net/rashid/archive/2009/04/03/asp-net-mvc-best-practices-part-2.aspx
http://www.lostechies.com/blogs/jimmy_bogard/archive/2009/04/24/how-we-do-mvc.aspx
http://codeclimber.net.nz/archive/2009/04/17/how-to-improve-the-performances-of-asp.net-mvc-web-applications.aspx  http://blog.maartenballiauw.be/post/2009/05/06/More-ASPNET-MVC-Best-Practices.aspx

si,bineinteles, eu 😉

Cum sa faci un javascript in ASP.NET 2.0 care sa poata sa afiseze ceva pe un alt site

Ok, titlul e cam naspa . Sa incerc sa o iau altfel : Orice site de informatii are un script js prin care alte site-uri preiau informatia(bineinteles, este o chestie de reclama)

Si iar revin la marota mea,www.infovalutar.ro, care avea preluare de curs prin PHP,Java, .NET, Python – dar nu avea prin Jscript (ceea ce majoritatea competitorilor aveau ) . Asa ca am fost fortat sa ma gindesc 😉

OK, imi trebuie un js care sa fie interpretat O chestie simpla era sa fac un js care sa se interpreteze pe server – dar nu aveam chef:

  1. Sa ma rog de fiecare data de hosting
  2. Sa trec js-urile printr-un proces suplimentar( chiar daca stiu ca asp.net verifica existenta tag-urilor si, daca nu sunt, lasa neprelucrat…)

E clar ca problema ar trebui inversata – si anume, avut “ceva” care se interpreteaza pe server care sa intoarca rezultatul . Clara solutia acum : un generic handler, de tipul ashx, care intoarce document.write(“text”);

Doua sfaturi:

  1. Aveti grija sa escapati “ in text , daca ati pus document.write(“text”).Daca nu puneti asa ceva, va afiseaza o pagina goala.
  2. Incercati sa il faceti cit mai generic posibil  – s-ar putea sa aveti nevoie de el si la alte proiecte.

Referitor la 2, eu am obtinut 2 clase:

clsTable – care preia niste stari de tipul cellpadding,backgroundcolor, tdbgcolor etc – astfel incit fiecare sa poa sa isi preia informatia avind culorile/fontul personalizat

clsConnect :ConfigurationSection : care intoarce datele din BD . E deriata din ConfigurationSection  astfel incit sa o pun in web.config(app.config) si sa uit de ea.

Demo gasiti la http://infovalutar.ro/webmaster.aspx . Daca cineva poate crede ca este util, voi posta si sursele

Ce am mai facut in ultimul timp

  1. Am reparat preluarea cursului valutar de la BCE – si in consecinta am ajuns la : Nu folosi niciodata double.parse fara sa folosesi si NumberFormatInfo ( de ex., NumberFormatInfo ni= System.Globalization.CultureInfo.CreateSpecificCulture("en-US").NumberFormat). Dai de belele mari …fara sa stii de ce si cind
  2. Am gasit protocolul pentru gtalk , astfel incit sa creez un bot de gtalk – dar, avind in vedere ca imi trebuia un serviciu, am cautat o solutie online.  Asa ca  am gasit http://www.imified.com – nu trebuie sa faci decit o pagina cu Response.Write in care sa interceptezi  Request["msg"]  si sa scrii inapoi cu Response.Write. Atentie : Numele botului de google este numele initial de la imified…
  3. Am facut un add-on de Firefox pentru cursul valutar – incredibil cit de usor se poate face, daca ai grija sa iti setezi un nou profil pentru firefox …Il gasiti la adresa https://addons.mozilla.org/en-US/firefox/addon/12395 (trebuie selectat “Let me install this experimental add-on “)

Ca sfaturi de dezvoltare :

    • Plecati de la un alt plugin simplu ( downloadati cu IE, schimbati extensia din xpi in zip, extrageti continutul si cititi codul sursa. Modificati in install.rdf em.id (merge un guid sau o adresa de email) – si celelalte
    • Creati-va o pagina Web care sa va trimita raspunsul corect pentru plugin( eu am avut noroc cu “azi” : http://infovalutar.ro/bnr/azi/eur )
    • creati-va un nou profil pentru firefox ( firefox.exe –ProfileManager  – ATENTIE : firefox trebuie sa fie inchis)
    • In profilul nou, in directorul extensions , puneti-va un fisier text cu numele de la em:id de la punctul 1 si inauntru numele directorului in care este pligin-ul decomprimat
    • Porniti firefox cu profilul nou.
    • Inchideti profilul, modificati XUL,js si ce o mai fi. Porniti firefox cu profilul nou. Daca e bine, de la capat…
    • Ca sa il publicati pe add-onurile de pe Mozilla, trebuie sa :
      • Va faceti un cont
      • Creeati o poza cu add-onul in functiune
      • Dupa il submiteti, puneti poza, scoateti “experimental” si va rugati ca cineva sa va faca un review

 

Ce sa spun decit ca, daca aveti deja aceste 2 chestii (bot+ add-on) integrate, va puteti lauda cu un site interactiv!

Doua observatii legate de add-onurile Mozilla:

  1. Gasiti favoritele mele la adresa https://addons.mozilla.org/en-US/firefox/user/1941896
  2. Daca instalati add-on de firefox pentru cursul valutar de la https://addons.mozilla.org/en-US/firefox/addon/12395, va rog sa ii faceti si un review… Poate scap de experimental…

Optimizarea site-urilor ASP.NET MVC cu Fiddler ,Yahoo Slow si Google Page Speed si Search Engine Optimization Toolkit

Disclaimer : Acest post se refera la un website obisnuit, la care nu ai acces la configurarea de pe server(de ex., nu ai acces la IIS Compression) si trebuie sa ti le faci singur

Marota mea favorita, www.infovalutar.ro, se incarca destul de greu prima oara. Asa incit a trebuit sa ii fac o optimizare. Site-ul fiind facut cu ASP.NET MVC, prima optimizare chioara am facut-o cu [OutputCache] pe controller. Asta cacheuia si pagina, si datele …tot. Ca sa vad cum cachuieste, am gasit un Html.Substitution cu care pot sa afisez chestii ne=cache-uite (modificat un pic, ca poate am nevoie sa scriu stringul EXACT asa cum este …):

public static object Substitute(this HtmlHelper html, MvcCacheCallback cb, bool Encode)
        {
            if (Encode)
            {
                html.ViewContext.HttpContext.Response.WriteSubstitution(
            c => HttpUtility.HtmlEncode(
                cb(new HttpContextWrapper(c))
            ));

            }
            else
            {
                html.ViewContext.HttpContext.Response.WriteSubstitution(
         c => (
             cb(new HttpContextWrapper(c))
         ));

            }
            return null;
        }

 

OK, apoi am inceput sa ma gindesc la optimizari … Mai intii imaginile, apoi css-urile + jscript, apoi redirect-urile . Asa ca am zis ca cel mai bine este sa incerc cu un tool – si primul ales este Fiddler. Asa ca am vazut redirecturi (301)la greu  .De ce le aveam ? pentru ca aveam probleme la controlere pentru uppercase/lowercase la argumente si m-am decis ca toate sa fie cu lowercase , asa incit pusesem in global.asax

protected void Application_BeginRequest(Object sender, EventArgs e)
       {
           // If upper case letters are found in the URL, redirect to lower case URL.
           if (Regex.IsMatch(HttpContext.Current.Request.Url.ToString(), @"[A-Z]") == true)
           {
               string LowercaseURL = HttpContext.Current.Request.Url.ToString().ToLower();

               Response.Clear();               
               Response.Status = "301 Moved Permanently";
               Response.AddHeader("Location", LowercaseURL);
               Response.End();
           }
       }

Asa ca am intervenit in site si am schimbat ca toate sa fie cu litere mici (un .ToLower() la linkuri a ajuns)

Dar am revenit la problema initiala: mai aveam nevoie sa fie cache-uite imaginile,css-urile si jscripturile. E adevarat, ma puteam uita in Fiddler(click pe fisier=>inspectors )

032811_Print Screen

sa vad care e cache-uita, care e zip-uita – dar aveam nevoie de un tool general. Asa ca , pentru mine , combinatia ideala a ajuns : Firefox + Firebug + Yahoo Slow + Google Page Speed. OK,daca vi le-ati instalat pe toate atunci puteti incepe cu yahoo Slow – si, ca sa ma dau bun iata cum arata la mine:

 

035121_Cursurile Banca Nationala a RomanieiBNR  Mozilla Firefox_active

Va rog sa observati ca am Grade A pentru “Small Site or Blog” . OK, ceea ce recomanda Yahoo Slow si nu faceam era:

  1. Put CSS at top
  2. Put Javascript at bottom
  3. Compress components with Gzip
  4. Make fewer HTTP requests.

1 si 2 sunt relativ usor – mai modifica site master-ul  si gata.

 

3.Cum facem Compress components with Gzip ? Am cautat rapid – si am gasit, in final, asta GZip and Deflate Compression Filter for ASP.Net MVC . Am pus-o, mergea bine pe masina mea, dar cum am pus-o in productie , cum a dat eroarea asta . Asa ca a trebuit sa renunt la OutputCache – si sa fac caching pe ASP.NET Cache – dar asta doar la datele din BD .Acum mergea perfect! Dovada : nu a mai aparut pagina la Yahoo Slow  – iar , inspectata cu fiddler => inspector , a aparut Gzip.

4.OK, acum era problema de optimizare a imaginilor – mai exact, vroiam ca imaginile sa ramine in cache-ul browser-ului, astfel incit sa nu se mai downloadeze inca o data. Pentru asta trebuia sa setez expiration la image  -dar cum, daca nu am acces la server ? Ca de obicei, se rezolva cu o indirectare: Sa zicem ca imaginile sunt in folder-ul flags. Atunci il pus sa treaca printr-un controller:

public static void RegisterRoutes(RouteCollection routes)
        {
            routes.MapRoute("flags", "flags/{id}.ashx",
                new { controller = "imagini", action = "flags" });

//alte date        

}

Iar fiecare imagine se va prelungi cu un ashx ( adica .gif.ashx) :

<img width="30" height="20" src="/<% ="flags/" + curLoop.IDMoneda.ToLower() + ".gif.ashx"%>" />

In controller-ul respectiv pe actiunea flags, redam imaginea :

            Response.Cache.SetExpires(DateTime.Now.AddDays(300));//
            Response.Cache.SetCacheability(HttpCacheability.Public);
            Response.Cache.SetValidUntilExpires(false);
            Response.AddHeader("content-disposition", "inline; filename=" + filename);

             Response.ContentType = content;

Response.WriteFile(file);

OK, acum iarasi Fiddler si presto : imaginile erau cache-uite aproximativ 1 an.

Dar cum sa fac cu Jscriptul ca sa fie si compresat si cache-uit ? Pai – acelasi lucru – sa treaca printr-un Controller si sa ii aplic Gzip prin clasa CompressFilter.

Aici am avut cea mai mare problema : de acasa imi dadea ca js e compresat – la servici ca nu. Innebunisem – si totul s-a rezolvat cu

//proxy servers in between may cache            
Response.AppendHeader("Vary", "Accept-Encoding");

Bun, deja ma miscam mai usor  in sensul ca pagina, de la citiva bun kb ajunsese la a doua iteratie la < 10 (ma rog, cu reclame < 15)

image

Daca apasati pe Statistics o sa vedeti 2 grafice : una de la prima incarcare si una de la a doua incarcare (in care nu se mai incarca cache-ul de la prima)– Daca numarul de Http Requests si numarul de Kb este acelasi – atunci aveti o problema. La mine este 48 / 6 Requests – cu 84 / 13 Kb.

Daca ati facut pina aici  – e super! Acum sa mai aplicam doar Google Page Speed – acesta este doar cireasa de pe tort

image

Cu el am mai optimizat din imagini , m-am uitat ca

  1. puteam optimiza prin minify “There is 173.8kB worth of JavaScript. Minifying could save 4.7kB (2.7% reduction).” – ma rog, doar pentru primii veniti – ceilalti au oricum cache-ul…
  2. ca ar trebui sa fac “This page makes 43 parallelizable requests to infovalutar.ro. Increase download parallelization by distributing these requests across multiple hostnames:” – mda, si de unde bani  ? Si yahoo vorbeste de CDN-uri

OK, ultimul tool de care voiam sa va vorbesc este Search Engine Optimization Toolkit. Este o scula extraordinara pentru incepatorii ca mine, se integreaza de minune in VISTA . Il rulati odata pe site-ul vostru(din IIS,vedeti Scott http://weblogs.asp.net/scottgu/archive/2009/06/03/iis-search-engine-optimization-toolkit.aspx ) , puteti creea robots.txt, sitemap si vedea multe altele. Oricum, postul lui Scott http://weblogs.asp.net/scottgu/archive/2009/06/03/iis-search-engine-optimization-toolkit.aspx  spune tot – si face analiza chiar pe site-ul propriu! Nu spun pe www.infovalutar.ro cit mi-a gasit!

Oricum, daca faceti site-uri, e bine de avut in trusa de dezvoltator!