Adatok csoportosítása és megjelenítése – ASP.NET + Dlinq

A feladat: adott a Northwind adatbázis Customers táblája, amiből az adatokat csoportosítva, csoportonként megszámlálva kell megjeleníteni egy weblapon, hierarchikus listában.

Először is, kell hozzá egy pofás Dlinq lekérdezés, amihez a szükséges osztályokat SqlMetal-lal legyártottuk:

    var query = ( from c in dc.Customers
                  group c by c.Country into g
                  select new
                  {
                    Country = g.First().Country,
                    Customers = g.OrderBy( p => p.ContactName ),
                    CustomerCount = g.Count()
                  }
).OrderByDescending( r => r.CustomerCount ).ThenBy( r => r.Country );

Igazi állatorvosi ló a query-k világában! Így kell olvasni:

Vedd a Customers táblát, csoportosítsd be a sorait a Country mező szerint és a csoportokat nevezd g-nek, majd minden csoportnak hozz létre egy új objektumot (aminek a típusát nem mondom meg, csinálj újat), melynek legyen három mezője:

  • A Country mező értéke legyen a csoport első elemének Country mezőjének értéke,
  • A Customers mező legyen egy tömb, elemei egyezzenek meg a csoport elemeivel, csak épp a ContactName mező szerint rendezve,
  • A CustomerCount mező pedig tartalmazza a csoport elemeinek számát,

Ezek után rendezd az eredményt a CustomerCount mező szerint csökkenő sorrendbe, azon belül pedig a Country mező szerint növekvő sorrendbe.

A legszebb az egészben, hogy akkor is működik, ha több táblát kapcsolunk össze és a Customers helyére például egy Orders tömböt teszünk.

Ha mindezt szép HTML kimenettel akarjuk megjeleníteni, akkor két egymásba ágyazott Repeatert használhatunk. Ha a szép kód annyira nem fontos, akkor szinte bármilyen vezérlő megteszi, hiszen egyszerű adatkötésről van szó:

    <asp:Repeater ID="repCountries" runat="server">
        <HeaderTemplate><ul></HeaderTemplate>
        <ItemTemplate>
            <li>
                <%# Eval( "Country" ) %> (<%# Eval( "CustomerCount" ) %> db)
                <asp:Repeater runat="server" DataSource='<%# Eval( "Customers" ) %>'>
                    <HeaderTemplate><ul></HeaderTemplate>
                    <ItemTemplate>
                        <li><%# Eval( "ContactName" ) %></li>
                    </ItemTemplate>
                    <FooterTemplate></ul></FooterTemplate>
                </asp:Repeater>
            </li>
        </ItemTemplate>
        <FooterTemplate></ul></FooterTemplate>
    </asp:Repeater>

Nem marad más hátra, mint az összes kódot összerakni egybe:

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

    Northwind dc = new Northwind( connStr );
    var query = ( from c in dc.Customers
                  group c by c.Country into g
                  select new
                  {
                     Country = g.First().Country,
                     Customers = g.OrderBy( p => p.ContactName ),
                     CustomerCount = g.Count()
                  }
).OrderByDescending( r => r.CustomerCount ).ThenBy( r => r.Country ); this.repCountries.DataSource = query; this.repCountries.DataBind();

Már lehet is tesztelni!

 

Mindössze két apró problémám van az egésszel:

1. Bár egyetlen táblán dolgozunk, az adatbázis műveletek száma = országok száma + 1. Sajnos ha egyszerű adatkötést használunk, akkor a hagyományos ADO.NET-tel sem tudjuk egyszerűbben megoldani (ha valaki meg tudja, írja meg!).

  • Az országokhoz kapcsolódóak nem túl bonyolult lekérdezések, ezt kapja meg az adatbázis:
    exec sp_executesql N'SELECT [t0].[CustomerID], [t0].[CompanyName], [t0].[ContactName], 
[t0].[ContactTitle], [t0].[Address], [t0].[City], [t0].[Region], [t0].[PostalCode],
[t0].[Country], [t0].[Phone], [t0].[Fax]
FROM [Customers] AS [t0]
WHERE ((@x1 IS NULL) AND ([t0].[Country] IS NULL)) OR
((@x1 IS NOT NULL) AND ([t0].[Country] IS NOT NULL) AND (@x1 = [t0].[Country]))
ORDER BY [t0].[ContactName]',N'@x1 nvarchar(3)',@x1=N'USA'
  • Engem az a bizonyos +1, az első lekérdezés jobban zavar, ami szerintem elég randára sikeredett (bár a végrehajtási tervben már szebbnek látszik):
    SELECT [t4].[value] AS [Country], [t4].[Country] AS [Country2], [t4].[CustomerCount]
    FROM (
        SELECT (
            SELECT [t3].[Country]
            FROM (
                SELECT TOP 1 [t2].[Country]
                FROM [Customers] AS [t2]
                WHERE (([t1].[Country] IS NULL) AND ([t2].[Country] IS NULL)) OR 
(([t1].[Country] IS NOT NULL) AND ([t2].[Country] IS NOT NULL) AND ([t1].[Country] = [t2].[Country])) ) AS [t3] ) AS [value], [t1].[Country], [t1].[value] AS [CustomerCount] FROM ( SELECT COUNT(*) AS [value], [t0].[Country] FROM [Customers] AS [t0] GROUP BY [t0].[Country] ) AS [t1] ) AS [t4] ORDER BY [t4].[CustomerCount] DESC, [t4].[value]

2. Az eredményt nem lehet közvetlenül TreeView-hoz kötni, mert a TreeViewnak hierarchikus adatforrásra van szüksége. Azt sem ördöngősség megírni, de még több munka és nem kapunk hozzá semmilyen VS támogatást. Aki még nem írt hierarchikus adatforrást, az lehet, hogy hamarabb készen lesz, ha végigiterál az eredményhalmazon és kódból tölti fel a fa csomópontjait.

Ezek után szerintem mindenki döntse el maga, hogy használja-e a Dlinq-et vagy sem, de aki teheti, írja meg a véleményét, mert kíváncsi vagyok!

Technorati tags: LINQ, DLINQ, ASP.NET

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s