Aplikace pro zamlouvání sedadel (část 2)

2. díl - Aplikace pro zamlouvání sedadel (část 2)

Tomáš Herceg       08.02.2011       C#, VB.NET, ASP.NET WebForms, ASP.NET/IIS, .NET       12203 zobrazení

V této části si vysvětlíme základy používání LINQ to SQL a napíšeme si jednoduchou třídu, která bude vracet data, která potřebujeme ve stránce, a rezervovat jednotlivá sedadla.

V minulé části tohoto seriálu jsme si vytvořili základní infrastrukturu naší aplikace – nastavili jsme membership providera, vytvořili přihlašovací stránku, nastavili oprávnění pro stránku Default.aspx. Dnes napíšeme metody, které z databáze vytáhnou informace o dané koncertní síni, vytáhnou seznam a stav obsazenosti sedadel a provedou rezervaci sedadla pro daného uživatele.

Práce s daty

Odbočka: Častým nešvarem vývojářů bývá, že se snaží vše namatlat do codebehindu stránky. Bohužel ne vše, co funguje, je taky správně a vhodně vyřešeno. Aby byl kód v budoucnu udržovatelný, je velmi dobré dodržovat dva principy – DRY a SRP (ono je jich ve skutečnosti víc, ale tyto nám zatím stačí).

DRY je zkratka z “Don’t Repeat Yourself” (neopakuj se), což v praxi znamená vyhnout se opakujícímu se kódu - pokud to samé dělám v aplikaci víckrát, měl bych to umístit do nějaké metody. V případě, že bude potřeba v tomto kódu udělat změnu, nebudu to muset dohledávat na padesáti místech.

SRP je zkratka “Single Responsibility Principle” (princip zodpovědnosti za jednu věc), který v kostce říká, že každá třída by měla dělat jen jednu věc, která by měla být poměrně jasně vymezena. Naše stránka je taky jen třída a rozhodně by neměla dělat všechno (tahat data z databáze a zároveň vytvářet řádky a buňky tabulky).

Minimálně tahání dat z databáze by bylo vhodné do samostatné třídy, protože tohle nejsme schopni udělat deklarativně. Stránka má data prezentovat, čili se bude starat o generování buněk tabulky a řešení, co se stane, když na ně někdo klikne. Ale samotné získávání dat a změny stavu sedadel oddělíme.

Protože jsem si již odvykl psát SQL dotazy ručně a na všechno používám ORM (nástroj, který zpřístupní obsah databáziepomocí objektů), použijeme pro práci s daty LINQ to SQL. LINQ to SQL je technologie, která se objevila s příchodem .NET Frameworku 3.5. Velmi podobnou (trochu robustnější) technologií je Entity Framework, pokud se naučíte jednu z nich, tu druhou zvládnete hravě.

Pokud jste LINQ to SQL ještě nikdy před tím neviděli, nezoufejte, je to snadné. Do projektu přidejte novou položku typu LINQ to SQL Classes a pojmenujte ji Opera.

image[3]

V designeru, který se objeví, přidáme tabulky z naší databáze – v okně Server Explorer se připojte k databázi aplikace, rozbalte složku Tables a naše tabulky přetáhněte do většího bílého prostoru:

image[7]

Po uložení nám tento designer vygeneruje několik tříd. První třídou je tzv. datový kontext, bude se jmenovat OperaDataContext (protože náš soubor se jmenuje Opera.DBML). Tento kontext obsahuje kolekce Halls, Seats a Users, které reprezentují tabulky v naší databázi, a pár dalších věcí, které nás ale nemusí tolik zajímat.

Dále se vygeneruje třída Hall s vlastnostmi HallId, SeatRows a SeatColumns, která navíc ještě bude obsahovat kolekci Seats, protože mezi tabulkami Halls a Seats v databázi máme vazbu 1:N. Obdobně se vygeneruje třída Seat, která bude mít vlastnosti odpovídající jednotlivým sloupcům této tabulky, a dále ještě vlastnost Hall typu Hall (objekt koncertního sálu z tabulky Halls) a vlastnost User typu User, která reprezentuje přiřazeného uživatele, je-li jaký.

Jak tyto třídy vypadají můžete vidět v souboru Opera.designer.cs, ale nic v něm neměňte, protože Visual Studio vám tento soubor při nejbližší příležitosti přepíše.

Pointa celého tohoto řešení je, že místo toho, abych ručně vytvářel SqlConnection a SqlCommand, psal ručně SELECT dotaz a procházel vrácené řádky přes SqlDataReader, vytvořím instanci třídy OperaDataContext a sáhnu si do kolekce Seats, což mi vrátí rovnou kolekci objektů typu Seat. A protože to má v názvu LINQ, znamená to, že nad touto kolekcí můžeme dělat dotazy, takže pokud v tabulce bude sedadel pět milionů (extra super mega cool velká opera), nebudeme je muset načítat všechny, ale sáhneme si jen na záznamy, která nás zajímají.

V tomto článku budu z LINQu používat jen základy, které pochopí každý, pokud vás zajímá, jak dělat pokročilejší dotazy, navštivte stránku 101 LINQ Samples.

Třída pro práci s databází

Nyní si napíšeme vlastní třídu SeatManager, která bude interně používat LINQ to SQL třídy. Od aplikace budeme potřebovat tři věci – vytáhnout detail koncertní síně, vytáhnout seznam všech sedadel a jejich stav, a zarezervovat sedadlo konkrétnímu uživateli.

Základní prototyp této třídy by mohl vypadat nějak takto:

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Linq;

/// <summary>
/// Třída, která se stará o zamlouvání sedadel a zjišťování jejich stavu
/// </summary>
public class SeatManager
{

/// <summary>
/// Vrací instanci datového kontextu
/// </summary>
private OperaDataContext CreateDataContext()
{
return new OperaDataContext();
}

/// <summary>
/// Vrátí objekt reprezentující koncertní sál s daným ID
/// </summary>
public Hall GetHall(int hallId)
{

}

/// <summary>
/// Vrátí seznam sedadel a velikost mřížky
/// </summary>
public IEnumerable<Seat> GetSeats(int hallId)
{

}

/// <summary>
/// Zkusí rezervovat dané sedadlo pro daného uživatele a vrátí, zda-li se to podařilo
/// </summary>
public bool TryReserveSeat(int seatId, int userId)
{

}
}

Třída obsahuje jednu metodu, která jen vytvoří nový datový kontext a vrátí jej. Možná vám to může připadat jako zbytečnost, ale ze zkušenosti tuto factory metodu doporučuji – nikdy nevíte, kdy budete potřebovat každé instanci datovém kontextu něco nastavit (například parametrizovat connection string atd.), a hledat v aplikaci každé new OperaDataContext je otravné.

Dále zde máme tři metody – GetHall, GetSeats a TryReserveSeat. Z komentáře je jasné, co dělají, první vrací objekz Hall s daným HallId, druhá vrací kolekci sedadel v dané hale (opět podle daného HallId), a třetí zkusí zarezervovat sedadlo pro daného uživatele.

Dotazování nad daty v LINQ to SQL

Kód první metody může vypadat například takto:

     /// <summary>
/// Vrátí objekt reprezentující koncertní sál s daným ID
/// </summary>
public Hall GetHall(int hallId)
{
using (var dc = CreateDataContext())
{
// vytáhnout sál s daným ID
return dc.Halls.Single(h => h.HallId == hallId);
}
}

Datový kontext vždy používejte pokud možno s konstrukcí using, která zajistí, že jakmile ji opustíte (jakkoliv, ať už výjimkou, klauzulí return, break nebo třeba goto), na kontextu se zavolá Dispose, čímž se uzavře příslušné spojení do databáze. Nechávat spojení otevřená není dobrý nápad, ona se sice za čas uvolní sama (až Garbage Collector usoudí, že je ten správný čas), ale do té doby by mohla dojít a aplikace by házela výjimky.

Uvnitř using bloku máme jen jeden řádek. To, co je za klauzulí return, je jedna z forem LINQ dotazu – dc.Halls je kolekce všech koncertních sálů v databázi. Na této kolekci voláme metodu Single, která z ní vybere právě jeden záznam (vrací výsledek typu Hall). To, co je uvnitř jako parametr, totiž h => h.HallId == hallId, je tzv. lambda funkce.

Ano, teď se možná chytáte za hlavu – takže Single je funkce, která dostává jako parametr funkci? Přesně tak. LINQ je koncept, který se inspiroval tzv. funkcionálním programováním, kde je toto běžné. Ve funkcionálních jazycích de facto neexistují proměnné, vše je funkce (to, co v ”normálních jazycích” považujeme za proměnnou, je ve funkcionálním jazyce funkce bez parametrů, která vrací pořád tu samou hodnotu).

Ta lambda funkce, o které byla řeč, je jen zkrácený zápis následujícího kódu. Schválně jsem žlutě označil to, co je pro deklaraci této funkce podstatné:

 bool MojeFunkce(Hall h)
{
return h.HallId == hallId;
}

A co že to dělá? Metodu Single spouštíme na kolekci objektů typu Hall. Single má za úkol objekty vyfiltrovat a vrátit ten jediný, který vyhovuje dané podmínce (všimněte si, že naše funkce vrátí true jen pokud je ID testované haly, která je v proměnné h). Naše funkce vrátí true jen tehdy, pokud je hodnota v proměnné hallId rovna HallId objektu v proměnné h. Je to prostě jen v C# zapsaná podmínka WHERE HallId = @p0, kde místo p0 se dosadí hodnota z proměnné hallId.

Obdobně bychom mohli vyfiltrovat haly podle toho, jestli mají více než 4 řady sedadel, podmínka by vypadala h => h.SeatRows > 4. Nebo haly, které mají víc než 500 sedadel – h => h.SeatRows * h.SeatColumns > 500. Proměnná h není nikde deklarována – její typ se uhodne podle kolekce, na které metodu Single spouštíme – bude typu Hall. Jméno proměnné h si zvolíme sami tak, že jej napíšeme před šipku =>, a samotná proměnná h platí jen ve výrazu za šipkou. V lambda funkcích ale můžeme používat proměnné z metody, uvnitř které lambda funkce je, tomuto principu se říká closure, to je třeba případ proměnné hallId.

Jinak lambda funkci obecně můžeme třeba přiřadit do proměnné, nebo ji zavolat (stačí za ni napsat otevřenou závorku a předat jí parametry, tak jako normální funkci).

 var mojeFunkce = (int a, int b) => a + b;
var dvacetjedna = mojeFunkce(15, 6);

Uvnitř metody Single si můžete představit foreach cyklus, které projde všechny položky kolekce, na níž ji spouštíme, pro každou zavolá naši funkci, a pokud ta funkce vrátí true, tak je to hledaný prvek – metoda Single navíc zkontroluje, že je jen jeden, a na konci jej vrátí (výsledek je tedy typu Hall).

Takhle by to fungovalo, pokud by dc.Halls byla obyčejná kolekce. Ale ona je to chytrá kolekce, která implementuje rozhraní IQueryable, díky kterému zjistí, že se na ní zavolala metoda Single s nějakou podmínkou. Tu podmínku takovou složitou magií přeloží do SQL a vyplivne dotaz SELECT * FROM Halls WHERE HallId = @p0, přičemž do parametru p0 dosadí hodnotu, kterou máme v proměnné hallId. Díky této složité magii druhý řádek v naší ukázce kódu spustí dotaz proti databázi a do proměnné hall uloží objekt, který bude reprezentovat daný řádek z databáze.

Pokud na IQueryable kolekci voláme metody, kolekce si jen “pamatuje, co se na ní volalo”, ve skutečnosti si staví jakýsi strom dotazu (tzv. expression tree). Jakmile na ni zavoláme foreach cyklus, tak celý tento strom převede do SQL, spustí, a vrátí výsledky.

 

Kód metody GetSeats bude vypadat takto:

     /// <summary>
/// Vrátí seznam sedadel a velikost mřížky
/// </summary>
public IEnumerable<Seat> GetSeats(int hallId)
{
using (var dc = CreateDataContext())
{
// vrátit sedadla v sále a natáhnout rovnou i jejich uživatele
var dlo = new DataLoadOptions();
dlo.LoadWith<Seat>(s => s.User);
dc.LoadOptions = dlo;

// zavolat ToList, aby se dotaz provedl
return dc.Seats.Where(s => s.HallId == hallId).ToList();
}
}

První tři řádky uvnitř bloku s DataLoadOptions prozatím ignorujme a podívejme se na poslední řádek return dc.Seats.Where(s => s.HallId == hallId).ToList().

Where je velmi podobné jako Single, ale jeho výsledkem není jeden objekt, ale opět kolekce (počítá s tím, že podmínku splňuje více objektů). A není to ledajaká kolekce, je to opět IQueryable, takže na tento dotaz můžeme řetězit další operace, jako třeba další Where, OrderBy atd.

Už víme, že IQueryable kolekce si jen pamatují, co jsme s nimi dělali, a samotný dotaz spustí až ve chvíli, kdy na ní zavoláme foreach. To musíme udělat předtím, než zrušíme datový kontext, takže na té kolekci zavoláme metodu ToList. Metoda ToList uvnitř foreach cyklus obsahuje, takže v okamžiku, kdy ji na kolekci zavoláme, dotaz se vyhodnotí a my dostaneme klasickou kolekci List<Seat>, která obsahuje vrácené výsledky.

V našem případě se na databázi spustí něco jako SELECT * FROM Seats WHERE HallId = @p0, kde p0 bude hodnota vlastnosti HallId našeho objektu. Kdybychom ale dotaz psali ručně, vidíme, že budeme ještě potřebovat uživatelské jméno toho, kdo si dané místo zaregistroval. Tímto dotazem ale dostaneme jen jejich UserId. Pokud bychom chtěli i uživatelské jméno a dotaz jsme si psali sami, přidali bychom JOIN na tabulku Users, takže by dotaz byl SELECT * FROM Seats LEFT JOIN Users ON Seats.UserId = Users.UserId WHERE HallId = @p0.

A to právě zařizují ty tři řádky, které jsme ignorovali – vlastnost LoadOptions datového kontextu určuje instrukce pro přednačítání tabulek. Metodou LoadWith jsme řekli, že vždycky, když natáhneme objekt typu Hall, budeme k němu chtít načíst i jeho vlastnost User, tedy vazbu na tabulku Users.

Pokud toto nastavení použijeme, do seznamu se nám vrátí kolekce objektů Seat, jejichž vlastnost User bude naplněná příslušným objektem User, anebo v ní bude null, pokud sedadlo zabrané není.

Pokud bychom toto přednačtení neudělali a sáhli na vlastnost User, LINQ to SQL by neměl jinou možnost než pro ni spustit separátní SQL dotaz a co to může udělat je vidět například v tomto článku. V praxi by to vypadalo, že bychom nespustili 2 SQL dotazy, ale 26 dotazů – jeden na halu a jeden na seznam sedadel (ty děláme vždycky), a pak pro každé sedadlo něco jako SELECT * FROM Users WHERE UserId = @p0. Pomocí výše uvedených 3 řádků se zeptáme na halu a následně na její sedadla a zároveň jejich uživatele, což bude daleko rychlejší.

Navíc pokud bychom na vlastnost User sáhli až ve chvíli, kdy je datový kontext zrušen (mimo using blok), dostali bychom po čumáku výjimkou, protože příslušné databázové spojení by bylo již uzavřené.

Při psaní LINQ dotazů si tedy musíme pořádně rozmyslet, které všechny navázané záznamy budeme potřebovat, a natáhnout je, například pomocí DataLoadOptions.

Rezervace místa

Kód druhé metody bude mít za úkol najít sedadlo s daným ID, zkontrolovat, jestli již není zabrané, a pokud není, zabrat jej (nastavit UserId na předanou hodnotu a uložit změnu).

Vnitřek metody TryReserveSeat bude vypadat takto:

     /// <summary>
/// Zkusí rezervovat dané sedadlo pro daného uživatele a vrátí, zda-li se to podařilo
/// </summary>
public bool TryReserveSeat(int seatId, string username)
{
using (var dc = CreateDataContext())
{
// vytáhnout sedadlo s daným ID
Seat seat = dc.Seats.Single(s => s.SeatId == seatId);

// pokud je již zabrané, vrátit false
if (seat.UserId != null)
return false;

// zjistit ID uživatele a zabrat sedadlo
var user = dc.Users.Single(u => u.UserName == username);
seat.UserId = user.UserId;

// uložit změny
try
{
dc.SubmitChanges();
}
catch (ChangeConflictException)
{
// změnu provedl v tu samou chvíli někdo jiný
return false;
}

// povedlo se
return true;
}
}

Na prvním řádku uvnitř bloku získáme stejně jako v předchozím případě sedadlo s daným ID. Podíváme se na jeho vlastnost UserId (pozor, vlastnost User by spustila SQL dotaz do tabulky Users, nenastavili jsme přednačítání, protože ho nepotřebujeme). Protože UserId v databázi je typu INT, ale může obsahovat NULL, v C# je reprezentována typem int?, což je zkratka za Nullable<int>. Do tohoto typu můžeme uložit buď null (které je jinak určeno pouze pro referenční typy), nebo číslo.

Pokud tam není null, tak sedadlo již má někdo jiný. V opačném případě do UserId nastavíme ID, které jsme dostali jako parametr. Zatím se ale nic neděje, žádný SQL dotaz se nespouští – datový kontext si jen poznamená, že tenhle objekt se změnil. Až ve chvíli, kdy si zavoláme SubmitChanges, všechny změněné objekty daného kontextu se uloží do databáze (vygenerují se UPDATE příkazy). Vše se provede v transakci, takže buď se všechono povede, nebo databáze vůbec nebude změněna, o to se stará databázový server.

Tady nastává ještě jedno úskalí – vzhledem k tomu, že webové prostředí je silně konkurenční, dva uživatelé mohou rezervovat to samé místo v tu samou chvíli nebo velmi krátce po sobě. Vzhledem k tomu, že každý HTTP požadavek se ve webové aplikaci zpracovává v separátním vlákně, může se tato metoda provádět zároveň víckrát. Obě dvě vlákna by si tedy mohla z databáze vytáhnout sedadlo, které je neobsazené, a naráz jej změnit.

Existují dva přístupy, jak se s tím vypořádat. První řešení je pštrosí způsob – strčíme hlavu do písku a neřešíme to, poslední zarezervovaný vyhrává. Druhé řešení (tzv. optimistic concurrency), které používá LINQ to SQL, vyhodí výjimku, pokud se objekt změnil a již neodpovídá tomu, co jsme načetli. My tuto výjimku zachytíme (jedná se o ChangeConflictException) a řekneme, že sedadlo jsme nezarezervovali. To je řešení, které funguje a je dobré se jej držet. Někdy stačí pštrosí způsob, ale u rezervace míst je dobré zajistit, že když se mi nezobrazila chyba, sedadlo je moje.

To by tedy bylo krátké intro do LINQu, určitě o něm v nejbližší době vyjde jeden nebo více článků, jelikož se jedná o technologii, která není zdaleka tak používaná, jak by býti mohla.

Pro tento díl je to vše, příště si ukážeme, jak ve stránce s použitím naší třídy vygenerovat tabulku sedadel, jak buňku tabulky rozšířit o událost Click a jak to celé dát dohromady.

 

hodnocení článku

1 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Všechny díly tohoto seriálu

4. Jak na dlouhotrvající úlohy 26.01.2012
3. Aplikace pro zamlouvání sedadel (část 3) 13.02.2011
2. Aplikace pro zamlouvání sedadel (část 2) 08.02.2011
1. Aplikace pro zamlouvání sedadel (část 1) 04.02.2011

 

 

 

Nový příspěvek

 

Diskuse: Aplikace pro zamlouvání sedadel (část 2)

V první řadě bych chtěl pochválit za moc pěkný úvod do LINQ. Osobně jej hojně využívám jak ve funkci LINQ pro Objekty tak LINQ pro SQL či LINQ pro XML a jsem s touto technologií dosti spokojen.

Ještě maličkost:

return hall.Seats.Where(s => s.HallId == hallId).ToList()

by asi mělo být:

return dc.Seats.Where(s => s.HallId == hallId).ToList()

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Jo, máte pravdu, díky za opravu.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Prosím jak by se pro String napsal dotaz ve stylu LIKE:

 return dc.tab_Budovies.Where(b => b.Souhrn == BudovaPopis  ); 

něco jako

 b.Souhrn Like '%' + BudovaPopis + '%' 
nahlásit spamnahlásit spam 0 odpovědětodpovědět

Po hodně dlouhém hledání odpovím sám sobě (pro podobné zoufalce začátečníky jako jsem já):

 return dc.tab_Budovies.Where(b => b.Souhrn.Contains(BudovaPopis)).ToList(); 
nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Aplikace pro zamlouvání sedadel (část 2)

Dobrý den,

díky moc za bezva článek, měl bych jen 2 drobnosti:

1. var mojeFunkce = (int a, int b) => a + b;

- mojeFunkce nemůže být implicitně typovaná lokální proměnná, musí být definovaná s konkrétním typem (delegátem)

2. Je potřeba dát si pozor u metody, která pracuje (a volá Dispose()) s DataContextem a zároveň vrací získaný objekt(y). Pokud volající kód změní některou s vlastností daného objektu, tyto změny už poté nelze jednoduše propagovat zpět do DB, protože není DataContext, který by "sledovat" daný objekt.

Ve Vašem případě to není problém (uvedený kód nic takového nedělá), zmiňuju to jen pro úplnost...

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Druhý bod v článku zmiňovaný je, proto uvnitř taky volám ToList.

Ano, var není schopen z obsahu lambda funkce doplnit datové typy. Nechtěl jsem to už komplikovat vysvětlováním typů Func a Action, ale v brzké době se pokusím popsat to podrobněji v seriálu o LINQu, který plánuji.

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Aplikace pro zamlouvání sedadel (část 2)

Chtěl jsem si vyzkoušet práci s využitím

var dlo = new DataLoadOptions();
             dlo.LoadWith<Seat>(s => s.User);
             dc.LoadOptions = dlo;

kde bych načetl všechny sedadla a k nim i uživatele (Jejich jména jako kdybych tabulky propojil pomocí Join v SQL). Ale dostal jsem se do problému jak danou kolekci vrátit.

Jelikož návratový typ je Seat

public IEnumerable<Seat> GetSeats(int hallId)

a ten obsahuje jen vlastnost UserId a tím pádem nemůže uložit hodnoty UserName. Jak by se mělo postupovat abych nemusel vytvářet samostatnou třídu, která by například dědila vlastnosti třídy "Seat" a rozšiřovala ji o jednu vlastnost UserName.

Předem moc díky za odpověď

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Třída Seat má vlastnost User, kterou se dostanete na UserName.

seat.User.UserName

Pokud byste chtěl zněit uživatele přiřazeného k tomu sedadlu, musíte ale nastavit UserId u sedadla (ID uživatele podle jména si najít separátním dotazem, jako je to v metodě pro rezervaci sedadla).

nahlásit spamnahlásit spam 0 odpovědětodpovědět

Diskuse: Aplikace pro zamlouvání sedadel (část 2)

Zdravím.

Bylo by možné nějak jednoduše vysvětlit či rozebrat pro a proti k užití jednotlivých způsobů k získávání dat z SQL? Proč používat LINQ oproti code behind kódu jste jasně řekl, ale máme ještě klasické SqlDataSource či Stored procedury na SQL serveru. Přiznám se, že dodnes úplně nechápu (a to fakt není nějaká zatvrzelost) jaké mi LINQ přinese výhody oproti výše zmíněnému. Moc rád bych slyšel nějaký kvalifikovaný a pochopitelný názor z praxe.

Díky moc!

Vacan

nahlásit spamnahlásit spam 0 odpovědětodpovědět

1. Výhoda nepoužití SqlDataSource je, že všechno nemáte namatlané v jedné stránce.

Co kdybyste chtěl ty samé dotazy mít na více stránkách jen s drobnými změnami? Budete na každé mít dva téměř stejné kusy kódu, což se obtížně udržuje a spravuje.

2. Při použití LINQ nemusíte psát SQL dotazy, ale píšete VB.NET nebo C# kód, který podporuje daleko lepší IntelliSense.

3. Pokud se jednou naučíte LINQ, můžete ho použít nejen proti databázi, ale úplně ty stejné dotazy fungují třeba nad klasickými kolekcemi v .NETu, nad XML atd.

4. V případě, že byste kód v třídě SeatManager psal klasicky pomocí SqlConnection, SqlCommand a SqlDataReader, bude ho daleko více, nebude tak přehledný a názvy sloupečků budete mít v kódu uvedené jako string. Když v nich uděláte překlep, přijdete na to až v době, kdy aplikace běží (a musíte se k té chybě doklikat, klidně se může stát, že si jí nevšimnete), zatímco když uděláte překlep v LINQ dotazu, tak vám to řekne rovnou kompilátor.

5. Dotazy se dají skládat dynamicky. Pokud chcete do SQL dotazu programově přidávat dejme tomu podmínky, skončíte s nějakou šílenou nudlí kódu. LINQ dotazy se dají za sebe skládat a do SQL se převádějí až ve chvíli, kdy na ně zavoláte foreach.

// vybrat všechny koncertní sály; pokud je zadáno jméno, tak jen ty, které obsahují zadaný řetězec
var dotaz = dc.Halls;
if (string.IsNullOrEmpty(TextBox1.Text))
   dotaz = dotaz.Where(h => h.Name.Contains(TextBox1.Text))

// podle toho, jestli je vyplněn TextBox1.Text se spustí buď:
// SELECT * FROM Halls 
// nebo
// SELECT * FROM Halls WHERE Name LIKE '%' + @p0 + '%'
foreach (var hall in halls) ...
nahlásit spamnahlásit spam 0 odpovědětodpovědět
                       
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