LINQ: Příklad použití operátoru group by

Tomáš Holan       8. 10. 2013       LINQ       5595 zobrazení

Předpokládejme, že máme datovou strukturu obsahující tyto tři tabulky:

Smeny

(Přitom jsou hodnoty pole smeNazev v tabulce Smena jedinečné a podobně jsou jedinečné dvojice hodnot sc_IDSmeny, sc_DenVTydnu v tabulce SmenaCasy.)

Z těchto tabulek budeme chtít načíst data najednou tímto jedním SQL dotazem:

SELECT smeIDSmeny, sc_IDSmenaCasy, scpIDSmenaCasyPrestavka, smeNazev, smeOznaceniSmeny, smeTypSmeny, smeNocniDruhyDen, smePoznamka, smePlatnostDo,
       sc_DenVTydnu, sc_PracovniDobaOd, sc_PracovniDobaDo, sc_ZakladniPracDobaOd, sc_ZakladniPracDobaDo, sc_VolitelnaPracDobaOd, sc_VolitelnaPracDobaDo, sc_AutomatickePrestavky, sc_FondSmeny,
       scpIntervalOd, scpIntervalDo, scpDelka
  FROM Smena LEFT OUTER JOIN
  	SmenaCasy ON SmenaCasy.sc_IDSmeny = Smena.smeIDSmeny LEFT OUTER JOIN
  	SmenaCasyPrestavka ON SmenaCasyPrestavka.scpIDSmenaCasy = SmenaCasy.sc_IDSmenaCasy
 ORDER BY smeNazev, sc_DenVTydnu, scpIntervalOd

Předpokládejme dále, že naše datová vrstva umí načtená data vrátit pouze v podobě “flat” sekvence objektů, jehož podoba odpovídá vracenému resultsetu:

internal class SmenaRow
{
    public int IDSmeny { get; private set; }
    public int? IDSmenaCasy { get; private set; }
    public int? IDSmenaCasyPrestavka { get; private set; }
    public string Nazev { get; private set; }
    public string OznaceniSmeny { get; private set; }
    public TypSmeny TypSmeny { get; private set; }
    public bool NocniDruhyDen { get; private set; }
    public string Poznamka { get; private set; }
    public DateTime? PlatnostDo { get; private set; }
    public int? DenVTydnu { get; private set; }
    public DateTime? PracovniDobaOd { get; private set; }
    public DateTime? PracovniDobaDo { get; private set; }
    public DateTime? ZakladniPracDobaOd { get; private set; }
    public DateTime? ZakladniPracDobaDo { get; private set; }
    public DateTime? VolitelnaPracDobaOd { get; private set; }
    public DateTime? VolitelnaPracDobaDo { get; private set; }
    public bool? AutomatickePrestavky { get; private set; }
    public int FondSmeny { get; private set; } 
    public DateTime? IntervalOd { get; private set; }
    public DateTime? IntervalDo { get; private set; }
    public int? Delka { get; private set; }
}

(Všimněte si, že vlastnosti odpovídající tabulkám SmenaCasy a SmenaCasyPrestavka jsou v objektu SmenaRow díky použití LEFT OUTER JOIN všechny “nullable”.)

Našim úkolem je přetransformovat sekvenci objektů SmenaRow do objektové reprezentace, která by odpovídala původním tabulkám:

internal class Smena
{
    public int IDSmeny { get; private set; }
    public string Nazev { get; private set; }
    public string OznaceniSmeny { get; private set; }
    public TypSmeny TypSmeny { get; private set; }
    public bool NocniDruhyDen { get; private set; }
    public string Poznamka { get; private set; }
    public DateTime? PlatnostDo { get; private set; }
    public IList<SmenaCasy> Casy { get; private set; }
}

internal class SmenaCasy
{
    public int IDSmenaCasy { get; private set; }
    public int DenVTydnu { get; private set; }
    public DateTime PracovniDobaOd { get; private set; }
    public DateTime PracovniDobaDo { get; private set; }
    public DateTime? ZakladniPracDobaOd { get; private set; }
    public DateTime? ZakladniPracDobaDo { get; private set; }
    public DateTime? VolitelnaPracDobaOd { get; private set; }
    public DateTime? VolitelnaPracDobaDo { get; private set; }
    public bool AutomatickePrestavky { get; private set; }
    public int FondSmeny { get; private set; } 
    public IList<SmenaCasyPrestavka> Prestavky { get; private set; }
}

internal class SmenaCasyPrestavka
{
    public int IDSmenaCasyPrestavka { get; private set; }
    public DateTime IntervalOd { get; private set; }
    public DateTime IntervalDo { get; private set; }
    public int Delka { get; private set; }
}

(Předpokládejme, že každá z těchto tříd má definovaný i konstruktor beroucí hodnoty všech vlastností v daném pořadí).

Pro řešení použijeme LINQ operátor group by (odpovídající metodě GroupBy) a to hned dvakrát. První vnější group by bude za tabuku SmenaCasy a druhý vnitřní group by za tabulku SmenaCasyPrestavka.

Dále protože datový zdroj vrací data seřazená, bude zde výhodné použít extension metodu WithAdjacentGrouping převzatou od Tomáše Petříčka. Její implementace je dostupná ve třídě zde.

Pro volání datového zdroje, který vrací sekvenci objektů SmenaRow, předpokládejme existující metodu DataSource.GetSmeny.

Kód, který bude provádět vlastní transformaci vstupní sekvence na výslednou kolekci objektů Smena pak bude následující:

var smeny = new List<Smena>();

foreach (var groupSmena in from row in DataSource.GetSmeny().WithAdjacentGrouping()
                           group row by row.IDSmeny)
{
    var casy = new List<SmenaCasy>();

    foreach (var groupCasy in from rowCasy in groupSmena
                              where rowCasy.IDSmenaCasy != null
                              group rowCasy by rowCasy.IDSmenaCasy.Value)
    {
        var prestavky = new List<SmenaCasyPrestavka>();

        foreach (var rowPrestavka in from rowPrestavka in groupCasy
                                     where rowPrestavka.IDSmenaCasyPrestavka != null
                                     select rowPrestavka)
        {
            var prestavka = new SmenaCasyPrestavka(rowPrestavka.IDSmenaCasyPrestavka.Value,
                rowPrestavka.IntervalOd.Value, rowPrestavka.IntervalDo.Value, rowPrestavka.Delka.Value);
            prestavky.Add(prestavka);
        }

        var smenaCasy = new SmenaCasy(groupCasy.Key, groupCasy.First().DenVTydnu.Value,
            groupCasy.First().PracovniDobaOd.Value, groupCasy.First().PracovniDobaDo.Value,
            groupCasy.First().ZakladniPracDobaOd, groupCasy.First().ZakladniPracDobaDo, groupCasy.First().VolitelnaPracDobaOd, groupCasy.First().VolitelnaPracDobaDo,
            groupCasy.First().AutomatickePrestavky.Value, groupCasy.First().FondSmeny.Value, prestavky);
        casy.Add(smenaCasy);
    }

    var smena = new Smena(groupSmena.Key, groupSmena.First().Nazev, groupSmena.First().OznaceniSmeny, groupSmena.First().TypSmeny,
        groupSmena.First().NocniDruhyDen, groupSmena.First().Poznamka, groupSmena.First().PlatnostDo, casy);
    smeny.Add(smena);
}

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:  
Heslo:  

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback