Dlinq alapok és SqlMetal, a Dlinq rabszolga

A márciusi Orcas CTP-t elindítva eleinte nehézkesnek tűnik a LINQ-kel történő ismerkedés, mert a 2006. májusi előzetestől eltérően itt már nincs külön LINQ projekt típus a Visual Studioban. 

Íme a szükséges első lépések:

  1. Hozzunk létre egy új konzol alkalmazást, például LinqApp néven.
  2. Referenciaként adjuk a projekthez a System.Data.Linq.dll-t, amit a C:WINDOWSMicrosoft.NETFrameworkv3.5.20209 mappában fogunk megtalálni. Érdekes módon a VS az Add Reference ablak .NET fülén nem sorolja fel, ezért használjuk a Browse fület és keressük meg a DLL-t.
  3. Júzingoljuk a System.Data.Linq névteret.

Miután ezzel így megvagyunk, bele is vethetjük magunkat a DLINQ-be. Ehhez először is adatbázisra lesz szükségünk, legyen a jól ismert Northwind, ami innen tölthető le.

Ahhoz, hogy a Northwindben lévő partner adatokat elérhessük, készítsünk egy Customer osztályt, ami C# szinten definiál egy partnert. A partnernek legyen ContactName, ContactTitle és City tulajdonsága, ezeket ugyanis tartalmazza az adatbázisban lévő Customers tábla.

Fontos, hogy az osztályunkat fel kell címkéznünk attribútumokkal, melyek azt jelzik, hogy ez az osztály melyik táblához tartozik és az egyes tulajdonságok a tábla mely oszlopához tartoznak. Ehhez a Table és Column attribútumokat használhatjuk, melyek a System.Data.Linq névtérben találhatóak. (Az Orcas VPC-ben lévő MSDN-ben megtalálhatjuk az osztályok összes tulajdonságát, itt csak a legegyszerűbbeket fogjuk használni.)

Ha mindent jól csináltunk, az osztály végül valahogy így fest, kihasználva az automatikus property gyártás új szintaxisát:

    using System;
    using System.Data.Linq;

    namespace LinqApp
    {
        [Table( Name = "Customers" )]
        class Customer
        {
            [Column]
            public string ContactName { get; set; }

            [Column]
            public string ContactTitle { get; set; }

            [Column]
            public string City { get; set; }
        }
    }

Definiáltuk a "sémát", már csak adatokkal kellene feltöltenünk. Ehhez persze kell egy connection string:

    string connStr = @"Data Source=.;Initial Catalog=Northwind;Integrated Security=True;";

Az objektumtér és az adatbázis között a kapcsolatot egy DataContext példány teremti meg, amely hasonló az ADO.NET Connection objektumához, ez ismeri például a connection stringet:

    DataContext dc = new DataContext( connStr );

A DataContext ojjektumtól kérhetjük el a táblákat is:

    Table<Customer> customers = dc.GetTable<Customer>();

A táblákon pedig lekérdezéseket definiálhatunk:

    var query = from c in customers        
                orderby c.ContactName
                where c.City == "London"
                select c;

A lekérdezés eredményét pedig feldolgozhatjuk, például így:

    foreach( Customer c in query )
    {
        Console.WriteLine( "{0}t{1}", c.ContactName, c.ContactTitle );
    }

Előfordulhat, hogy fordítás közben a következő hibaüzenetet kapjuk:

‘System.Data.Linq.Table<LinqApp.Customer>’ does not contain a definition for ‘OrderBy’ and no extension method ‘OrderBy’ accepting a first argument of type ‘System.Data.Linq.Table<LinqApp.Customer>’ could be found (are you missing a using directive or an assembly reference?)

A megoldás:

    using System.Linq;

Ezzel készen is van az első DLINQ alkalmazásunk!

 

Néhány dolgot mindenképp érteni kell:

1. Mi az a var, oda a típusosság?

Szó sincs róla! A var kulcsszó annyit jelent, hogy nem mondom meg a típust, kedves fordító, találd ki a jobb oldalról. A fordító tehát olyan típusúvá fogja tenni query változót, amilyennek azt a lekérdezés meghatározza. A lekérdezések egyébként System.Linq.IQueryable típusúak, ami lényegében egy felturbózott IEnumerable, ezért lehet forícselni.

2. Mikor fut a lekérdezés?

A LINQ egyik alapelve az úgynevezett deferred execution, azaz késleltetett lekérdezés, melynek lényege, hogy a lekérdezések nem akkor futnak, amikor definiáljuk őket, hanem amikor az eredményüket fel akarjuk dolgozni. Mivel a lekérdezések IEnumerable származékok, valójában akkor futnak, amikor végigiterálunk rajtuk.

Ezt egyébként a VS is tudja! Tessék betenni egy breakpointot a var és a foreach közé és megnézni a query változót az Autos ablakban! Az eredmények kibontása előtt ott a figyelmeztetés:

Expanding the Results View will enumerate the IEnumerable.

És valóban, ha SQL Profilerrel megnézzük, valóban csak akkor kap kérést az adatbázis szerver.

3. Milyen SQL utasításokra fordul a lekérdezés?

Ha nem akarunk SQL Profilerezni, akkor legegyszerűbben úgy tudhatjuk meg, hogy mit kell az adatbázisnak megennie, ha használjuk a DataContext Log tulajdonságát, aminek bármilyen TextWritert átadhatunk:

    dc.Log = Console.Out;

Jelen esetben ezt kapja meg az SQL Server:

    SELECT [t0].[ContactName], [t0].[ContactTitle], [t0].[City]
    FROM [Customers] AS [t0]
    WHERE [t0].[City] = @p0
ORDER BY [t0].[ContactName] -- @p0: Input NVarChar (Size = 6; Prec = 0; Scale = 0) NOT NULL [London]

Az IQueryable belső reprezentációja egy Expression objektum, amely az ún expression tree-t tartalmazza, tehát akár ezt is írhattuk volna (ha esetleg kedvelnénk a lambda expressionöket):

    var query = customers.OrderBy( c => c.ContactName ).Where( c => (c.City == "London") );

4. Tényleg minden táblához kell írnom egy osztályt?

Osztály kell, de nem kell megírni, van ugyanis osztálygyártó rabszolga, úgy hívják: SqlMetal.exe. Ez a kis jószág a C:Program FilesMicrosoft Visual Studio 9.0SDKv3.5Bin mappában található, ami sajnos nincs bent a PATH-ban a március CTP image-ben, így vagy hozzávesszük, vagy nyitunk egy SDK command promptot és belépünk a Bin almappába. Az alkalmazás egyékbént egyetlen 98KB-os exe, amit át is másolhatunk a projekt mappánkba.

Van neki jó sok paramétere, a mi példánkban használhatjuk így:

    SqlMetal.exe /server:. /database:Northwind /code:Northwind.cs /namespace:LinqAppMetal /pluralize

Azt hiszem minden kapcsoló magáért beszél, a gyakorlatban még valószínűleg a /sprocs kapcsolót fogjuk használni, amivel tárolt eljárások hívását is egy csapásra metódus hívásokká burkolhatjuk. A generált fájlban találunk egy DataContextből származó Northwind osztályt, illetve minden egyes táblának egy saját osztályt. Érdemes megnézni, hogy miben más a generált Customer osztály, ahhoz képest, amit mi írtunk!

Az SqlMetal által generált 2599 sornyi forráskódot felhasználva a következőre egyszerűsödik az adatbázis lekérdezésünk:

    Northwind dc = new Northwind( connStr );
    var query = from c in dc.Customers
                orderby c.ContactName
                where c.City == "London"
                select c;

Így már szerintem sokkal barátságosabb a feladat.

Technorati tags: ,

Vélemény, hozzászólás?

Adatok megadása vagy bejelentkezés valamelyik ikonnal:

WordPress.com Logo

Hozzászólhat a WordPress.com felhasználói fiók használatával. Kilépés / Módosítás )

Twitter kép

Hozzászólhat a Twitter felhasználói fiók használatával. Kilépés / Módosítás )

Facebook kép

Hozzászólhat a Facebook felhasználói fiók használatával. Kilépés / Módosítás )

Google+ kép

Hozzászólhat a Google+ felhasználói fiók használatával. Kilépés / Módosítás )

Kapcsolódás: %s