2007. július havi bejegyzések

Mit validál a validator?

"Hát a Text property-t, nem?" Na ennyire nem egyszerű a helyzet, de kezdjük inkább az elején. Ha írunk egy saját kontrollt, majd feldobunk belőle egy példányt az egyik WebFormunkra, majd mellérakunk például egy RequiredFieldValidatort, akkor futtatáskor először a következő hibaüzenetet fogjuk kapni:

The ControlToValidate property of ‘rfvPhone’ cannot be blank.

Nincs semmi meglepetés, mindenki tudja, hogy a ControlToValidate tulajdonságot be kell állítani, mégis mindenki elfelejti az első fordítás előtt 😉 Amikor azonban megpróbáljuk beállítani ennek a tulajdonságnak az értékét, csalódottan fogjuk tapasztalni, hogy a Studioban a Properties ablakban nem tudjuk kiválasztani a saját vezérlőnket. Ne adjuk fel, bátran írjuk be a nevét mezőbe! Következő futtatás, következő hibaüzenet:

Control ‘ctrlPhone’ referenced by the ControlToValidate property of ‘rfvPhone’ cannot be validated.

Na, itt már valami gond van, nem lehet validálni a kontrollunkat. Idézzük fel, mit tanultunk a validátorokról: ha sajátot akarunk írni, akkor a BaseValidatorból kell származtatni kell és felül kell definiálni az EvaluateIsValid metódust. Nézzük mit találunk ebben, ha a RequiredFieldValidator az alany:

    protected override bool EvaluateIsValid()
    {
        string controlValidationValue = base.GetControlValidationValue( base.ControlToValidate );
        return ( ( controlValidationValue == null ) || 
!controlValidationValue.Trim().Equals( this.InitialValue.Trim() ) ); }

Az a bizonyos kutya tehát a BaseValidator osztály GetControlValidationValue metódusában van elásva, hantoljuk ki:

    protected string GetControlValidationValue( string name )
    {
        Control component = this.NamingContainer.FindControl( name );
        if( component == null )
        {
            return null;
        }
        PropertyDescriptor validationProperty = GetValidationProperty( component );
        if( validationProperty == null )
        {
            return null;
        }
        object obj2 = validationProperty.GetValue( component );
        if( obj2 is ListItem )
        {
            return ( (ListItem) obj2 ).Value;
        }
        if( obj2 != null )
        {
            return obj2.ToString();
        }
        return string.Empty;
    }

Tehát valahogy megszerezzük a tulajdonságot és elkérjük az értékét. Ha az érték típusa ListItem, akkor annak a Value tulajdonsága a fontos, egyébként egyszerűen megToStringezzük. Ebből tehát az következik, hogy bármilyen típusú tulajdonságot lehet validálni, hiszen ToString metódusa mindennek van.

Hogyan lesz meg a validálandó tulajdonság? A legelső sorban megkeressük magát a kontrollt. Itt érdemes megfigyelni, hogy csak az aktuális naming containeren belül keresünk, ezért kell a validátort a kontroll "közelébe" tenni. A keresett tulajdonságot a BaseValidator GetValidationProperty metódusa adja vissza, mégpedig így:

    public static PropertyDescriptor GetValidationProperty( object component )
    {
        ValidationPropertyAttribute attribute = 
(ValidationPropertyAttribute) TypeDescriptor.GetAttributes( component )[
typeof( ValidationPropertyAttribute ) ];
if( ( attribute != null ) && ( attribute.Name != null ) ) { return TypeDescriptor.GetProperties( component, (Attribute[]) null )[ attribute.Name ]; } return null; }

Keressük tehát azt a tulajdonságot, amelyet az osztályhoz rendelt ValidationPropertyAttribute Name tulajdonsága meghatároz. Magyarul nem csak a Text property-t lehet validálni, hanem bármit, csak mondjuk meg szegény validátornak, mégpedig a kontrollunkhoz rendelt attribútummal:

    [ValidationProperty( "LocalNumber" )]
    public partial class PhoneControl : System.Web.UI.UserControl
    {...}

Ha ezt megtesszük, eltűnik a hibaüzenet, a kontrollunkhoz már nyugodt szívvel tehetünk validátorokat. Ennek az attribútumnak egyébként a következő a szintaxisa az MSDN-ben:

    [AttributeUsageAttribute( AttributeTargets.Class )] 
    public sealed class ValidationPropertyAttribute : Attribute

Mivel az attribútumot osztályhoz rendeljünk és nem lehet több példányban felhasználni, csak egy tulajdonságot validálhatunk. Ha mégis megpróbáljuk többszörösen használni, fordítási hibát kapunk:

error CS0579: Duplicate ‘ValidationProperty’ attribute

Ezek után joggal merül fel a kérdés, vajon a Properties ablak is ez alapján dolgozik? Reflectorral megnézve a BaseValidator ControlToValidate tulajdonságát azt vehetjük észre, hogy egy ValidatedControlConverternevű TypeConverter van hozzárendelve, aminek a FilterControl metódusát a következőképpen implementálták:

    protected override bool FilterControl( Control control )
    {
        ValidationPropertyAttribute attribute = 
(ValidationPropertyAttribute) TypeDescriptor.GetAttributes( control )[
typeof( ValidationPropertyAttribute ) ];
if( attribute != null ) { return ( attribute.Name != null ); } return false; }

Tehát azok a kontrollok fognak kiválaszthatóként megjelenni a Properties ablakban a ControlToValidate tulajdonságnál, amelyekhez van ilyen attribútum és annak a Name tulajdonsága is meg van adva. Reflectorral keresgélve a következő típusokat és validálandó tulajdonságaikat találtam:

  • HtmlInputFile: Value
  • HtmlInputPassword: Value
  • HtmlInputText: Value
  • HtmlSelect: Value
  • HtmlTextArea: Value
  • DropDownList: SelectedItem
  • FileUpload: FileName
  • ListBox: SelectedItem
  • RadioButtonList: SelectedItem
  • TextBox: Text

Na, ezért nem tudunk validátort tenni egy CheckBoxListre, amikor rá akarjuk kényszeríteni a felhasználót, hogy legalább egy elemet válasszon ki.

Ennyi tudással felvértezve visszemehetünk a Properties ablakba és megnézhetjük, hogy már ki lehet-e választani a saját kontrollunkat a validálandó kontrollok listájából, majd joggal kérdezhetjük, hogy miért nem? Aki tudja, kérem írja meg. Köszönöm!

 

Technorati tags: ,

Workflow Monitor layouttal

Aki használta már a Windows Workflow Foundationben a tracking service-t, biztosan találkozott már az SDK-ban lévő Workflow Monitor alkalmazással, ami lehetővé teszi, hogy a Visual Studiohoz hasonlóan, grafikusan jelenítsük meg a folyamataink állapotát. Mivel ez az alkalmazás forráskóddal együtt elérhető, remek példa arra, hogy megtanuljuk, hogyan hosztolhatjuk a WorkflowView kontrollt saját alkalmazásainkban. A feladat egyébként nem egyszerű, jobban járunk, ha a kész példából vesszük az osztályokat, többre is szükségünk lesz 😦

Van egy fontos dolog, amire ebben a kódban nem találunk példát, ez pedig a folyamatábrák layoutjának kezelése. Ez szekvenciális workflow esetén nem probléma, hiszen ott úgysem lehet mozgatni az activity-ket, állapotgépek esetén viszont jó lenne, ha a monitorban is úgy jelennének meg a folyamataink, ahogy azokat a Studioban megrajzoltuk. Ha erről nem gondoskodunk, akkor az összes állapot-dobozunk egymás alatt fog elhelyezkedni, keresztül-kasul behálózva az összekötő vonalakkal.

A tetszetős című Everything about re-hosting the Workflow Designer című MSDN cikkben találunk utalást arra, hogyan menthetjük el a layoutot fájlba és tölthetjük vissza a layoutot fájlból. Ezzel csak egy baj van, kell hozzá layout fájl! Nem is probléma, hiszen a VS készít nekünk olyat, ott van .layout kiterjesztéssel, pont arra van szükség. Csakhogy ha ezt a megközelítést választjuk, a layout fájlnak ott kell lennie a futtatandó alkalmazásunk mellett, azaz ezt is telepíteni kell. Amikor pedig a telepítőt készítjük, beleütközünk abba a problémába, hogy a forrásfában kénytelenek leszünk vagy saját build eventeket írni, vagy pedig duplikálni a layout fájlt. Egyik sem túl fincsi.

Az igazi megoldás az lenne, ha fel tudnánk használni ugyanazt a fájlt futási időben, amit a Studio is használ fejlesztési időben. Ha a Properties ablakban megnézzük a .layout fájl Build Actionjét, láthatjuk, hogy Embedded Resource lesz belőle, nincs más dolgunk tehát, mint onnan kiolvasni.

Először megpróbáltam közvetlenül kiolvasni a fájl tartalmát ugyanúgy, mint ahogy más erőforrást kezelni szoktunk. Mivel ez nem vezetett eredményre, természetesen Reflectorhoz nyúltam és az alábbi eredményre jutottam: a Loader.cs fájlban található a WorkflowDesignerLoaderből származó Loader osztály, ami a folyamatok betöltéséért felelős. Ebben írjuk felül az OnEndLoad eseménykezelőt:

  protected override void OnEndLoad( bool successful, ICollection errors )
  {
    base.OnEndLoad( successful, errors );

    IList layoutErrors = null;
    IWorkflowRootDesigner rootDesigner = 
(IWorkflowRootDesigner) ActivityDesigner.GetRootDesigner( base.LoaderHost ); Type type = rootDesigner.Component.GetType(); string manifestResourceName = type.Name + ".layout"; this.LoadDesignerLayoutFromResource( type, manifestResourceName, out layoutErrors ); }

Így már csak arra az egy szerelvényre van szükségünk, amiben a workflow definíció is található és ugyanúgy fog megjelenni a Monitorban, mint a fejlesztőkörnyezetben.

 

Paraméter validálás JavaScriptben

Szerver oldalon olyan kényelmes (IntelliSense) String.IsNullOrEmpty-t hívni, as-zel kasztolni és nullt vizsgálni, vagy éppen ArgumentNullExceptiont dobni, amikor egy metódus bemeneti paramétereit akarjuk ellenőrizni. Kliens oldalon, javascriptben egészen más a helyzet: minden leütött karakter és begépelt kódsor fáj, hiszen számtalan hibalehetőséget rejt magában.

A példa kedvéért vegyünk egy igazán egyszerű javascript metódust, amiben négyezetre akarjuk emelni a bemeneti paraméter értékét:

    function square( i )
    {    
        if( i === undefined )
            alert( "Meg kell adni a négyzetre emelendő számot!" );

        if( typeof( i ) !== 'number' )
            alert( "Csak számot lehet négyzetre emelni!" );
        
        if( isNaN( i ) )
            alert( "A megadott paraméternek számnak kell lennie!" );

        return i * i;
    }

Felugráló hibaüzenet hegyek, elég csak paraméter nélkül meghívni ezt a metódust. Ha ezt a mintát követjük, a hibakereséshez az egyetlen rendelkezésünkre álló információ a hibaüzenet. Na de melyik programozó ír ki értelmes hibaüzenetet önként és dalolva? smile_wink Főleg miután ennyi kódot volt kénytelen írni, hiszen ne felejtsük el, nem erősen típusos környezetben vagyunk!

Kicsit javíthatunk a helyezeten, ha használjuk a Microsoft AJAX Library-t, az ugyanis kiterjeszti az Error osztályt úgy, hogy kivételeket hajigálhassunk vele:

    function square( i )
    {    
        if( i === undefined )
            throw Error.argumentUndefined( "i", "Meg kell adni a négyzetre emelendő számot!" );

        if( typeof( i ) !== 'number' )
            throw Error.argumentType( "i", Object.getType( i ), Number, 'Csak számot lehet négyzetre emelni!' );
        
        if( isNaN( i ) )
            throw Error.argumentNull( "i", "A megadott paraméternek számnak kell lennie!" );

        return i * i;
    }

Akit érdekel, hogy érték ezt el Redmondban, bátran nézzen bele a MicrosoftAjax.debug.js fájlba, az Error osztály a 223. sor körül kezdődik. Ezeket a kivétel típusokat lehet megtalálni:

  • Sys.ArgumentException
  • Sys.ArgumentNullException
  • Sys.ArgumentOutOfRangeException
  • Sys.ArgumentTypeException
  • Sys.ArgumentUndefinedException
  • Sys.InvalidOperationException
  • Sys.NotImplementedException
  • Sys.ParameterCountException
  • Sys.ScriptLoadFailedException

Aki nem fél dokumentálatlan vizekre evezni, ennél átláthatóbb kódot is írhat. A Microsoft AJAX Library fájljait nézegetve feltűnhet egy Function._validateParams metódus, amivel a szinte minden metódus ellenőrzi a bemenő paramétereit. Nosza, használjuk fel, immár egy bonyolultabb példán, aminek több paramétere van:

    function square2( x, showResult, msg )
    {
        var e = Function._validateParams( arguments, [
            { name: "x", type: Number, integer: true },
            { name: "showResult", type: Boolean },
            { name: "msg", type: String, optional: true }
        ] );
        
        if( e )
            throw e;
        
        if( msg !== undefined )
            var result = String.format( msg, x * x );
        else
            var result = String.format( "Az eredmény: {0} * {0} = {1}", x, x * x );
        
        if( showResult )
            alert( result );            
        
        return result;
    }

A _validateParams metódusnak JSON formában adhatjuk át, hogy mit és hogyan szeretnénk ellenőrizni. Már-már deklaratív leírásnak is tekinthetjük. A következő lehetőségeink vannak:

  • name: a paraméter neve.
  • type: a paraméter típusa, String, Number, Boolean, Array vagy bármilyen saját típus.
  • mayBeNull: a paraméter értéke lehet-e null, az alapértelmezett false.
  • optional: elhagyható-e a paraméter, az alapértelmezett false.
  • parameterArray: a paraméter tömb, az alapértelmezett false.
  • integer: a paraméter egész szám, az alapértelmezett false.
  • elementType: ha a paraméter tömb, akkor a tömb elemeinek típusa.
  • elementInteger: ha a paraméter tömb, akkor ellenőrzi, hogy minden elem egész szám legyen.
  • elementDomElement: ha a paraméter tömb, akkor ellenőrzi, hogy minden eleme DOM elem legyen.
  • elementMayBeNull: ha a paraméter tömb, akkor ellenőrzi, hogy lehet-e null eleme vagy sem.

Nincs is más dolgunk, mint a metódus által visszaadott kivételt (a fenti példában e) jó messzi eldobni, erre szolgál a throw e.

Már csak egy kérdés maradt: hogyan kezeli a hívó a kivételt? Természetesen try-catch blokkal, majdnem ugyanúgy, mint szerver oldalon, például így:

    try
    {
        square2(); // Hibás, mert nincs paramétere
    }
    catch( e )
    {
        if( e.name === "Sys.ParameterCountException" )
            alert( "Nem jó: " + e.message );
    }

Itt is erősen típusos érzetünk lehet, a name tulajdonságból kiderül a kivétel típusa, a message pedig a szöveget tartalmazza. Bár a Microsoft AJAX Library támogatja a lokalizációt, a hibaüzenet alapértelmezett szövege sajnos nem lokalizálható, az ugyanis a Sys.Res osztálytól kap értéket smile_sad Ezt az osztályt szintén a MicrosoftAjax.debug.js fájlban találjuk, éppen a legvégén, az összes üzenet ott található sztringként, ha nagyon akarjuk, átírhatjuk.

A _validateParams metódus nagy áldás egy nem típusos környezetben. Remélem a Visual Studio 2008-ban remek IntelliSense is lesz már hozzá, de azt se bánnám, ha szerver oldalon is lehetne attribútumokat így használni.

 

Technorati tags: , ,

Tracking profile importálása szkriptből

Nincs jobb, mint amikor az embernek van egy olyan szkriptje kéznél, ami egyszerűen és átláthatóan teszi a dolgát és igazán megkönnyíti a fejlesztő munkáját. Az egyik gyakori feladat az adatbázis létrehozása, amit két módon lehet megtenni:

  • Adatbázis attachelésével. Ez egyszerű, csak éppen a változtatásokat nehéz követni benne fejlesztés közben. Tipikusan akkor választja az ember ezt a megoldást, amikor azt hiszi, hogy ezzel kevesebb gondja lesz, de később kiderül, hogy mégse.
  • SQL szkriptekkel. Ez kényelmesen szerkeszthető és verzionálható, csak éppen az a kérdés, hogyan fognak egyszerűen lefutni az SQL szkriptek.

Az utóbbi az érdekesebb, hogyan futtatunk SQL szkripteket parancssorból? Korábban az osql.exe vagy az isql.exe volt a jópajtás, az SQL Server 2005-től kezdve ezek helyét átvette az SQLCMD.

Például ha Windows Workflow Foundationt használunk, valószínűleg szükségünk lesz tracking és persistence adatbázisokra, amikhez csak az SQL szkripteket kapjuk a Microsofttól. Nosza gyűrjük be az alábbi néhány sort egy cmd fájlba és máris lehet duplán kattintva adatbázist gyártani (eltördeltem a hosszú sorokat):

    SQLCMD -S .SqlExpress -Q "CREATE DATABASE MyDB"

    SQLCMD -S .SqlExpress -d MyDB -i "C:WINDOWSMicrosoft.NETFrameworkv3.0
Windows Workflow FoundationSQLENSqlPersistenceService_Schema.sql" SQLCMD -S .SqlExpress -d MyDB -i "C:WINDOWSMicrosoft.NETFrameworkv3.0
Windows Workflow FoundationSQLENSqlPersistenceService_Logic.sql" SQLCMD -S .SqlExpress -d MyDB -i "C:WINDOWSMicrosoft.NETFrameworkv3.0
Windows Workflow FoundationSQLENTracking_Schema.sql" SQLCMD -S .SqlExpress -d MyDB -i "C:WINDOWSMicrosoft.NETFrameworkv3.0
Windows Workflow FoundationSQLENTracking_Logic.sql"

Eddig egyszerű volt, de mi van akkor, ha saját tracking profile-t is akarunk használni? Erről annyit érdemes tudni, hogy a profil egy XML fájl, aminek a tartalmát az UpdateTrackingProfile tárolt eljárás segítségével kell egy táblába betöltenünk.

Általánosabban megfogalmazva a feladatot: hogyan tudunk egy olyan tárolt eljárást futtatni, aminek egyik bemeneti paramétere egy fájl teljes tartalma?

A megoldás első lépését a BULK INSERT T-SQL utasítás jelenti. Ennek megadhatunk egy fájl útvonalat, aminek a tartalmát be tudja tölteni egy táblába. Sajnos van néhány nyűgje:

  • Csak tábla típusú változót szeret, tehát nem elég egy DECLARE, csak CREATE TABLE jó neki.
  • A betöltendő fájl útvonalát nem lehet változóban megadni, oda kell írni aposztrófok közé.
  • Ha egy kulturáltan megformázott XML-t akarunk betölteni, akkor minden egyes sor külön rekordba kerül, ami persze nem jó, ha végül egyetlen cellában szeretnénk látni az eredményt.

Íme a megoldás:

    CREATE TABLE #temp( profileXml nvarchar(max) )


BULK INSERT #temp FROM '$(ProfilePath)' WITH ( ROWTERMINATOR = '<<<' -- dummy terminator, hogy az egész fájlt felolvassa )

Csinálunk tehát egy temp táblát, mert mindenképp tábla kell. Az egész fájl beolvasását úgy oldjuk meg, hogy olyan sor elválasztó karaktert adunk meg, ami biztosan nincs a fájlban. A kérdés már csak az, hogy mi a $(ProfilePath)?

Az SQLCMD egyik remek szolgáltatása, hogy lehet változókat megadni, amiket ő behelyettesít. A fenti aposztrófok közé például így varázsolhatunk értéket (a példa kedvéért elhagytam a többi paramétert):

    SQLCMD -v ProfilePath="profile.xml"

Ezzel megint csak gondunk lesz: a BULK INSERT panaszkodik, hogy nem teljes elérési utat adtunk meg. Kérdés: hogyan lehet egy cmd fájlban megtudni az aktuális mappa elérési útvonalát? Meglepő módon a cd parancs és a cd környezeti változó épp ezt adja vissza. Szerencsére az SQLCMD is tud környezeti változókat kezelni, így csak ennyit kell módosítanunk:

    SQLCMD -v ProfilePath="%cd%profile.xml"

Nem maradt más hátra, mint az UpdateTrackingProfile tárolt eljárás meghívása, amihez a már jól ismert EXEC utasítást használjuk. A profil XML-en kívül át kell neki adnunk egy TypeFullName, egy AssemblyName, és egy Version paramétert, melyeknél ugyanúgy járhatunk el, mint a fenti ProfilePath esetén. Ezek típusát és hosszát a tárolt eljárásból lehet kilesni.

Végül nekem a teljes SQL szkript így ez lett:

    -- Bemeneti paraméterek
    DECLARE @TypeFullName nvarchar(128)
    DECLARE @AssemblyName nvarchar(256)
    DECLARE @Version varchar(32)

    SET @TypeFullName = '$(TypeFullName)'
    SET @AssemblyName = '$(AssemblyName)'
    SET @Version = '$(Version)'

    -- Belső változók
    DECLARE @profileXml nvarchar(max)
    CREATE TABLE #temp( profileXml nvarchar(max) )

    -- Tracking profile betöltése fájlból temp táblába
    BULK INSERT #temp 
      FROM '$(ProfilePath)'
      WITH
      (
        ROWTERMINATOR = '<<<'  -- dummy terminator, hogy az egész fájlt felolvassa
      )

    -- Tracking profile XML kiolvasása temp táblából
    SELECT @profileXml = profileXml FROM #temp

    -- Tracking profile adatbázisba mentése
    EXEC dbo.UpdateTrackingProfile @TypeFullName, @AssemblyName, @Version, @profileXml

    -- Temp tábla törlése
    DROP TABLE #temp

És a cmd fájl, ami felépíti a teljes adatbázist és beimportálja a profilt (eltördeltem a hosszú sorokat):

    SQLCMD -S .SqlExpress -Q "CREATE DATABASE SignowDB"

    SQLCMD -S .SqlExpress -d MyDB -i "C:WINDOWSMicrosoft.NETFrameworkv3.0
Windows Workflow FoundationSQLENSqlPersistenceService_Schema.sql" SQLCMD -S .SqlExpress -d MyDB -i "C:WINDOWSMicrosoft.NETFrameworkv3.0
Windows Workflow FoundationSQLENSqlPersistenceService_Logic.sql" SQLCMD -S .SqlExpress -d MyDB -i "C:WINDOWSMicrosoft.NETFrameworkv3.0
Windows Workflow FoundationSQLENTracking_Schema.sql" SQLCMD -S .SqlExpress -d MyDB -i "C:WINDOWSMicrosoft.NETFrameworkv3.0
Windows Workflow FoundationSQLENTracking_Logic.sql" SQLCMD -S .SqlExpress -d MyDB -i "InsertTrackingProfile.sql" -v Version=1.0.0
-v TypeFullName="MyNamespace.MyWorkflow1"
-v AssemblyName="MyLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=443f6c5b661c8da2"
-v ProfilePath="%cd%profile1.xml" SQLCMD -S .SqlExpress -d MyDB -i "InsertTrackingProfile.sql" -v Version=1.0.0
-v TypeFullName="MyNamespace.MyWorkflow2"
-v AssemblyName="MyLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=443f6c5b661c8da2"
-v ProfilePath="%cd%profile2.xml"

A legjobb az egészben, hogy mindezt nem csak fejlesztés közben lehet használni, hanem ezt a szkriptet be lehet építeni a telepítő alkalmazásba is.

 

Technorati tags: , ,

Webstatisztika Microsoft módra?

A címben szereplő kérdésre eddig is és most is nagyon rövid a válasz: a Microsoftnak nincs normális eszköze arra, hogy működő webhelyek forgalmát egyszerűen mérni és elemezni tudjuk. Az IIS Log Parser persze egy remek eszköz, annak való, aki imád szkripteket irogatni vagy text fájlokat feldolgozni és begyúrni valamilyen megjelenítő motor alá. Ez tehát nem kész megoldás, de néhány rááldozott hétvégével és éjszakával szép eredményeket lehet elérni vele.

Gyakran kérdezik meg tőlem, hogy akkor milyen eszközt használunk? Amit szinte mindenki, akinek olyan gyorsan implementálható és ingyenes megoldás kell, ami szinte mindent tud: Google Analytics. Egyszerűen zseniális, az új felhasználói felület pedig igazán könnyen használható. Az ember nem a hogyannal van elfoglalva, hanem a miérttel.

Fizetős termékből sok jó létezik a piacon, sokaktól hallottam, hogy a Deep Metrix LiveSTATS egy jól használható termék. Erre most mit találok a nyitólapukon:

We’re excited to update you about Microsoft’s activities since we acquired DeepMetrix.

Over the past year we’ve been focused on building the next generation of Web analytics. We’ve reached a transitioning phase, and are now rolling out our new service. Because of this rollout, we’re no longer updating DeepMetrix products.

Support services for hosted and installed DeepMetrix products will discontinue on January 31, 2008, and data collection for your hosted software will end on January 14, 2008. You may continue to use the unsupported installed software indefinitely.

This summer you’ll be invited to try the beta of our new Web analytics service-Project Gatineau. Some of the highlights of the beta include:
– Click and visitor tracking
– Marketing campaign reporting
– Conversion tracking
– Demographic and geographic segmentation
– Paid and natural search analysis

As customers of DeepMetrix and Web analytics, Microsoft values your business and hopes you’ll try our new Web analytics service.

We’ll let you know more as further information becomes available. For general questions or comments regarding DeepMetrix products or Microsoft Project Gatineau, please e-mail us at dms@microsoft.com.

Ezek után nem bírtam magammal és rákerestem a projekt nevére, természetesen a Google-ön. Úgy látszik sikerült elsiklani egy január 9-i esemény felett: Ian Thomas blogjában bejelentette, hogy létezik a Gatineau projekt a Microsofton belül. Valaki már rá is kattant a www.microsoft-analytics.com domain névre és itt találtam meg, hogy e-mailben lehet a bétára jelentkezni. Mivel a béta NDA-s, ezért valószínűleg nem sok részletet fogunk megtudni róla előre 😦

Érdekességként idelinkelek egy érdekes adatmegjelenítési megoldást, amibe keresgélés közben futottam bele…

 

Technorati tags: ,

Adatkötés + HtmlEncode

Gyakori feladat, hogy adatbázisból érkező adatokat szeretnénk megjeleníteni egy weboldalon. Mivel nagyon okosak vagyunk és tudjuk mi az a cross-site scripting, ezért szeretnénk bebiztosítani magunkat és becsomagolni a megjelenítendő sztringeket egy-egy Server.HtmlEncode hívásba.

Keressük a lehető legegyszerűbb megoldást:

1. Végigiterálunk az eredményhalmazon és minden mezőre jól meghívjuk a HtmlEncode-ot, majd az így kapott eredményt adatkötjük. Ez azoknak való, akik nem ismerik az adatkötést, de már megtapasztalták, hogy amit a varázsló generál, az működik.

2. Kicsivel jobb megoldás, ha a Control DataBinding vagy hasonló eseménykezelőjébe rakosgatjuk bele HtmlEncode hívásokat. Aki idáig eljut, az már hallott az eseményekről, piros pontot neki.

3. Deklaratívan beleírjuk az ASPX fájlba a Bind után. Sajnos itt explicit módon meg kell hívnunk a ToStringet, mert a Bind és az Eval objectet ad vissza, a HtmlEncodenak meg string bemenet kell. Némi plusszmunka árán deklaratív megoldáshoz jutottunk, ráadásul mivel ez megjelenítéshez kapcsolódó "logika", nem is baj, hogy nem zagyválja össze a code behind fájlunkat. Kedves dizájner és grafikus kollégák, kerüljétek ki, ami <% és %> között van.

4. Mind közül a legjobb, hogy ASP.NET 2.0-tól kezdve a Literal kontroll mindezt helyből tudja. Mit tanultunk a Literalról? Hogy ő a legbutább kontroll, gondolkodás nélkül kiköpi a Text tulajdonságát a HTML kimenetbe. Hát most már nem, ő is második osztályba lépett! Van neki egy Mode tulajdonsága, amit ha LiteralMode.Encode értékre állítunk, akkor a szöveget kódolva írja kimenetbe. Egyébként mindössze ez a sok okosság van a Literal Render metódusában, ez Reflectorban jól látszik:

    protected internal override void Render( HtmlTextWriter writer )
    {
        string text = this.Text;
        if( text.Length != 0 )
        {
            if( this.Mode != LiteralMode.Encode )
            {
                writer.Write( text );
            }
            else
            {
                HttpUtility.HtmlEncode( text, writer );
            }
        }
    }

Kéretik tehát szeretni a Literal kontrollt, mert most már nem csak gyors, de tényleg jól használható. Akkor miért használ mindenki mindenre Labelt? A legtöben azért, mert közvetlenül be lehet állítani a CSS osztályt, amihez egy span fog renderelődni. Ne vicceljünk már, tessék kézzel beleírni az ASPX-be, hacsak nem kell a CssClass tulajdonságot is kötni. Felmentést csak az kap, aki beviteli űrlapon használja, mégpedig az AssociatedControlID tulajdonsággal együtt!

Ennyi szépség után már csak egy dolog bántja a szemem: nem a LiteralModel.Encode az alapértelmezett, hanem a Transform, ami egyrészt a DefaultValue attribútumból, másrészt pedig a getter metódusból látszik:

    public LiteralMode get_Mode()
    {
        object obj2 = this.ViewState[ "Mode" ];
        if( obj2 != null )
        {
            return (LiteralMode) obj2;
        }
        return LiteralMode.Transform;
    }

Na de miért? Miért? Csak egy ötletem van, biztos kompatibilitási okokból, ugyanis a Mode tulajdonság új a .NET 2.0-ban. Na de nézzük, mire jó a Transform az MSDN szerint:

  • Encode: The literal control’s contents are HTML-encoded. 
  • PassThrough: The literal control’s contents are not modified. 
  • Transform: The literal control’s unsupported markup-language elements are removed. If the literal control is rendered on a browser that supports HTML or XHTML, the control’s contents are not modified. 

Magyarul egy normális böngésző esetén Transform=PassThrough, ami viszont tényleg megegyezik a Literal 1.1-beli viselkedésével. Mobil böngésző esetén viszont vigyázat, eltérő lehet a viselkedés!

 

Technorati tags: