2009. január 30., péntek

CLR Team Blog

Blogot indított a Common Language Runtime team: http://blogs.msdn.com/clrteam/.
Mindjárt egy érdekes cikkel indítanak a binderről.

Érdemes lesz követni.

Az élet értelme: 1 - avagy néha az is Error, ha még Warning sincs

A múltkori üres feltételes "utasításblokk"-kal kapcsolatban jutott eszembe ez a régi szösszenet, a =+ operátorról.
Mit ír ki az alábbi kód?

int i;
i = 41;
i =+ 1;
Console.WriteLine("Az élet értelme: {0}", i);
Hát azt, hogy "Az élet értelme: 1", miközben köztudott, hogy a helyes válasz nem ez. De miért? Miért nem működik a =+ operátor?
Hát azért, mert nincs olyan. += operátor van, ezt benéztük. De akkor miért nem kapunk fordítási hibát? Hogyhogy így megcserélve is működik?
Egyetlen whitespace odébbugrasztásával sokat lehet dobni a kód olvashatóságán:
i = +1;
Így már talán nem is kell magyarázat.

2009. január 26., hétfő

.NET Posters and CheatSheets

A posztereket, cheatsheeteket szeretjük, mert ha ilyennel tapétázzuk körbe irodánkat, az menő hatást kelt (legalábbis nem szakmabeli embertársainkban, a szakmabeliek előtt egyszerűen csak lebukás: képtelenek vagyunk megjegyezni az ASP.NET page lifecycle-t).
Nos:

A .NET Framework 3.5 fontosabb névterei és oszályai (beleértve a BCL-t, az ASP.NET-et, a foundation-öket, LINQ-et, stb.):
http://blogs.msdn.com/brada/archive/2008/01/12/net-framework-3-5-namespace-poster-updated.aspx

Visual C# Default key bindings (VS billentyűzet-kombók - legalább annyit számítanak, mint anno a Mortal Kombatban):
http://www.microsoft.com/downloads/thankyou.aspx?familyId=e5f902a8-5bb5-4cc6-907e-472809749973&displaylang=en

A Mix08 alkalmából megjelent Silverlight poszter:
http://blogs.msdn.com/brada/archive/2008/03/16/silverlight-2-developer-poster.aspx

RioterDeckers' HeadQuarter: Asp.net page lifecycle
http://blog.rioterdecker.net/photos/images/images/113/original.aspx

Johs Sheenan cheat sheetjei (.NET Format String Quick Reference, ASP.NET 2.0 Page Life Cycle & Common Events, Visual Studio 2005 Built-in Code Snippets)
http://john-sheehan.com/blog/net-cheat-sheets/

ASP.NET Runtime Cheat Sheet: HttpRequest, HttpRuntime, AppDomain and friends
http://duartes.org/gustavo/articles/Asp.net-Runtime-Cheat-Sheet-HttpRequest-HttpRuntime.aspx

ASP.NET Basics két oldalban:
http://www.learnvisualstudio.net/content/cheats/ASPNET_Basics.pdf
TFS, metodológia és SharePoint poszterek:
http://www.drp.co.za/Media/Posters/PostersPDF/tabid/62/Default.aspx
[Nem túl barátságos az oldal, a download linket kicsit keresni kell (a boxok jobb alján van), és érdemes IE-t használni a letöltéshez, Firefoxban malformed lesz a fájlnév (ebben az esetben a PDF-re való átnevezés segít).]

ASP.NET Resources cheat sheetek:
http://aspnetresources.com/downloads/ms_ajax_library_cheat_sheets1.zip
http://aspnetresources.com/downloads/MS%20Ajax%20Client%20Life-Cycle%20Events.pdf
http://aspnetresources.com/downloads/linq_standard_query_operators.pdf

RegEx cheat sheet:
http://www.addedbytes.com/cheat-sheets/download/regular-expressions-cheat-sheet-v2.pdf


A packetlife.net blog cheatsheet szekcióját pedig feltétlenül továbbítsuk hálózat-üzemeltetéssel foglalkozó kollégáinknak!
http://packetlife.net/cheatsheets/

Te miket ismersz?

2009. január 24., szombat

Kétszer kettő néha öt - avagy a Warning néha Error

Találós kérdés: vajon mit ír ki az alábbi kód?

int x = 2;
int y = 2;
bool never = false;
bool ever = false;
if (x * y == 5 && never && ever);
{
    Console.WriteLine("Kétszer kettő néha öt!");
}
Persze hogy azt, hogy "Kétszer kettő néha öt!", különben nem lenne róla post, de miért?

A szemfülesek észrevehetik a feltétel sorának a végén a pontosvesszőt. Aki nem szemfüles, de legalább figyel, azt a fordító Warning: Possible mistaken empty statement üzenete figyelmezteti (egyszersmind el is magyarázza helyzetet: az if után bizony nem a feltételesnek szánt utasításblokkunk van, hanem egy árva pontosvessző, azaz egy üres utasítás).

Aki meg se nem szemfüles, se nem figyel - mint ahogy én tettem -, csodálkozhat, hogy lehet kétszer kettő öt - a Console.WriteLine() ugyanis így mindig végrehajtódik.

2009. január 23., péntek

Netbook-barát MSDN

Gyakran szoktam szakirodalmat olvasni egy MSI Wind netbookon - ez, akárcsak a kategóriatársak szinte mindegyike, egy 1024x600-as felbontásra képes kijelzővel bír. PDF dokumentumok olvasására (főleg, ha egy CTRL+L-lel bekapcsoljuk a teljes képernyős módot) megfelelőnek mondható. Ha azonban egy olyan website-ot böngészünk, aminek az oldalain fejléc, menü, tartalomjegyzék is található - pl. az MSDN-t -, akkor szűkösnek bizonyulhat.


Hamar rááll az ember keze az F11-re, amivel a legtöbb browsert teljes képenyőre lehet nagyítani. Így megszabadulhatunk a böngésző menüsorától, egyéb eszköztáraitól, azonban a navigációs elemek még mindig sok helyet foglalnak az oldalon.
Van azonban egy printer friendly módja is az MSDN-nek, ami nem csak printer, hanem netbook friendly is!



Itt értelemszerűen (nyomtatásban nem sok hasznuk volna) nincsenek navigációs elemek, csak szépen formázott tartalom. Voila, fullscreen webes MSDN!

2009. január 22., csütörtök

SQL Cache ürítése

Az SQL Server igen szofisztikált cache-elési mechanizmussal rendelkezik (képes a memóriában tárolni a felparseolt query-ket, beolvasott lapokat, stb). Erre ritkán gondolunk úgy, mint egy probléma forrására - legtöbbszöt inkább öröm, hogy többedszeri futtatásra már akár nagyságrendekkel gyorsabban lefuthat a lekérdezésünk. Egy szituáció azonban van, amikor ez zavaró: ha teljesítmény-elemzést, optimalizálást próbálunk végezni, általában azt is szeretnénk látni, hogy mi van, ha egy "hideg" adatbázis-motor kapja meg a kérésünk.

Szerencsére ezek a cache-ek törölhetők: a DBCC FREEPROCCACHE segítségével a végrehajtási tervek cache-ét üríthetjük ki, a DBCC DROPCLEANBUFFERS segítségével pedig az eddig felolvasott adatokat invalidálhatjuk.

Érdemes tehát így kezdeni az optimalizáladó querynk futtatását:

DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS

SELECT
    Mezo1, Mezo2, ...
--    További bonyolult query...
Bánjuk viszont óvatosan ezekkel, amennyiben egy produkciós környezetben futó SQL Serverrel játszunk - nem biztos, hogy a felhasználóink örülni fognak az esetlegesen sokszorosára nyúló válaszidőknek.

2009. január 21., szerda

Tekerés SQL-ben - PIVOT nélkül

Egy érdekes problémába ütköztem ma. Adott egy adatbázis, elég speciális szerkezettel: nem egy-egy entitás-típushoz tartozik egy-egy tábla, hanem a tulajdonság-típusokhoz: egy tábla a hosszú szövegeknek, egy a binárisoknak... Ilyet akkor szokás csinálni, ha a séma gyakran változik (pl. azért, mert a felhasználó kezébe adjuk a séma módosításának lehetőségét).
A probléma (némileg leegyszerüsítve) ott kezdődött, hogy volt egy "FlatProperties" tábla, amiben a rövid, nvarchar(kevés) típusú tulajdonságok voltak tárolva. Ezekből sok volt egy sorban, Nvarchar1, Nvarchar2... és így tovább. Végtelen sok persze nem lehet, ez a limitáció úgy lett feloldva, hogy minden sor kapott még egy Page mezőt is, ezzel kvázi tényleg a végtelenbe tolva ki a lehetőségek számát.
Jó lett volna viszont, ha egy SQL-lel az egy adott entitáshoz tartozó, éppen minket érdeklő adatokat össze lehet gereblyézni egy sorba.

Futottam pár kört a PIVOT-tal, de úgy tűnt, nem ő lesz az én barátom.
Egy igazi svájcibicskára volt szükségem: a CASE-re. Ezzel meg lehet oldani, hogy az eredményhalmazban az aktuális Page indextől függő oszlopokat generáljunk.
Volt még egy kis bibi: egy entitáshoz továbbra is több sor tartozott, azaz a Page-ek megmaradtak külön soron. Nosza, még egy GROUP BY, méghozzá GROUP BY VersionId: az egyes csoportok máris az egyes adott enitiáshoz (jelen esetben verzióhoz) tartoznak. Végül valami ilyesmi lett:

SELECT
    VersionId,
    MAX (CASE WHEN Page = 0 THEN Nvarchar1 END) AS Nvarchar1_Page0,
    MAX (CASE WHEN Page = 0 THEN Nvarchar2 END) AS Nvarchar2_Page0,
    MAX (CASE WHEN Page = 1 THEN Nvarchar1 END) AS Nvarchar1_Page1,
    MAX (CASE WHEN Page = 1 THEN Nvarchar2 END) AS Nvarchar2_Page1,    
    MAX (CASE WHEN Page = 2 THEN Nvarchar1 END) AS Nvarchar1_Page2,
    MAX (CASE WHEN Page = 2 THEN Nvarchar2 END) AS Nvarchar2_Page2
FROM
    FlatProperties
GROUP BY
    VersionID

2009. január 7., szerda

Dobjuk meg az exceptiont!

Murphy általános érvényű törvénye a szoftverfejlesztésben sem kerülhető ki: ha valami félremehet, akkor félre is fog menni. Ezt szerencsére maguk a szoftverfejlesztők is tudják, és az idők során változatos módszereket dolgoztak ki arra, hogy ha egy függvény (metódus) hívása során valami hiba lép fel, akkor azt jelezzék a hívó felé.
Ez legtöbbször a 0 érték visszadása volt, vagy éppen, ha sokféle hiba léphetett fel, a 0 volt a jó, és a pozitív számok jelölték a hibakódot, de az is lehetett, hogy a -1 volt a hiba, és a többi a jó. Vagy fordítva.
Vagy lehetett bármi más magic numbert visszaadni a hibás végrehajtódást jelzendő - később, ahogy a számítógép-architektúrák fejlődtek, és az int egyre szélesebb lett, egyre tágabb tér nyílt egyre mágikusabb számok használatára.

Akkoriban a szoftverfejlesztők még acélos férfiemberek voltak, akik ezeket a számokat mind fejből tudták - legalábbis a visszaemlékezések szerint, valójában rengeteg időt (=pénzt) emésztett fel, hogy kiderítsék, hogy aznap épp mi jelenti a jót és mi a rosszat.

Nem csoda, ha ma ilyen módszerrel már csak Chuck Norris dolgozik - az objektum-orientált személet ebben is nagy váltást hozott. Az OOP-s srácok lényegesen optimistábbak voltak, és azt mondták, hogy hiba, az csak kivételes esetben fordulhat elő, és gyorsan kitalálták a kivétel-kezelést, ami ma már - apróbb különbségekkel ugyan, de - minden OOP nyelvben megtalálható. A kivétel-kezelés jó, a kivétel-kezelés szép. Két egyszerű ökölszabály:

  • ha hibába ütközünk, dobjunk exception-t
  • ha olyat hívunk, ami kivételt válthat ki, zárjuk try-catch blokkba
Ezt többnyire be is tarjuk, de néha azért felmerülhet bennünk: nem lenne egyszerűbb most simán egy null-t visszaadni itt, ha nem sikerül valami? Aztán majd a hívó oldalon lekezeljük... Ilyenkor általában a kisördög is megjelenik, és azt mondja: dehogynem, ráadásul mennyivel erőforrás-takarékosabb!, hisz a kivételobjektum megkonstruálása, a try-catch mechanizmus mind erőforrásokat igényel. Különben is, mi baj lehet?

Hogy mi? Vegyük például a következő kódrészletet:

Stream stream = MyFileManager.OpenFile("TheresNoSuchFile.txt");
using (StreamReader streamReader = new StreamReader(stream))
{
    Console.WriteLine(streamReader.ReadToEnd());
}
Egyszerű, megnyitjuk egy fájlt a saját FileManager-ünk segítségével, és végigolvassuk. De mi a helyzet, ha a megnyitni szándékozott fájl nem létezik?
Amennyiben erőforrás-takarékos módon egy null-t adtunk vissza, akkor kapunk egy közönséges NullReferenceException-t. Ez legalább annyira közönséges, mint a közönséges levesteknős, vagy a közönséges húsleves.
Egy biztos: túl sok mindenre nem fog fény derülni a kivételből, jótanácsként is maximum azt kapjuk, hogy használjuk a "new" kulcsszót új objektumpéldány létrehozásához.

Ha úgy érezzük, hogy azon már túl vagyunk, hogy a new operátorral ismerkedjünk, szálljunk magunkba, és nézzük meg mit csinálhatnánk jobban. Dobhatnánk pl. egy FileNotFoundException-t a remek fájlmegnyitó-függvényünkben, egy jól fejlett message-el, amiben elmagyarázhatjuk magunknak, hogy mit nem és miért nem. Hát nem szebb?

Persze van, amikor tényleg overkill az exception dobás, pl. egy statikus gyártófüggvényben lehet jobb megoldás a return null; (főleg, ha esetleg pont azért hoztunk létre, hogy ne a konstruktorból kelljen hányni a kivételt). Ez egyedi elbírálás kérdése, de jól gondoljuk meg, nem csinálunk-e magányos Chuck Norrist magunkból, csak hogy öt sor kóddal kevesebbet írhassunk.

Aki azt gondolná, ilyet csak amatőrök követnek el: ezt a hibát konkrétan a Microsoft is elkövette a .NET Framework 1.x verziójában (a 2.0 után javították). Ha az assemblynkbe nem beágyaztnuk, hanem hozzácsatoltunk egy erőforrást - azaz az mezei fájlként ott figyelt a .dll mellett, és szükség esetén a futtatókörnyezet megnyitotta és berántotta - pont ez volt a helyzet. Ha tényleg ott volt a fájl, semmi gond, de ha véletlenül (gonoszak direkt) letöröltük, akkor az 1.1 egy szép NullReference-szel örvendeztetett meg, és lehetett pislogni, hogy mi van. A 2.0 megmondja: nincs meg ez a fájl, öreg, nem lesz ez így jó.

A témára egyébként Richard Grimes Fusion Workshopjában bukkantam (egészen pontosan itt), ami most tök offtopic - a Fusion-ről, azaz a .NET assembly betöltő mechanizmusáról szól -, viszont szintén nagyon érdekes, érdemes elolvasni.