5. díl - SqlDataReader a připojená databáze v C# .NET

C# .NET Databáze SqlDataReader a připojená databáze v C# .NET

V minulém dílu našeho seriálu tutoriálů o databázích v C# .NET jsme se připojili k databázi a vypsali počet řádků v tabulce. V dnešním dílu se naučíme číst z databáze jednotlivé řádky.

Zpracování dat

Pokud potřebujete spustit nad databází dotaz, který vrací více záznamů (ten výše vracel jen jedno číslo), musíte ke zpracování výsledku dotazu využít třídu *DataReader. Instanci této třídy jako výsledek vrátí metoda ExecuteReader() třídy Command. Třída *DataReader umožňuje procházet výslednou množinu dat po jednotlivých záznamech. K tomu slouží metoda Read(). Přístup k jednotlivým atributům záznamu můžete získat pomocí indexu nebo názvu sloupce uvedeného v hranatých závorkách, případně pomocí metod Get..., které vám hodnoty rovnou převedou na požadovaný datový typ.

Příklad 4

Vypište na obrazovku všechna slovíčka. U slovíček budeme chtít vypsat sloupce: Id, Czech a English.

Řešení

SqlCommand prikaz = new SqlCommand("SELECT Id, Czech, English FROM Word", pripojeni);
pripojeni.Open();
SqlDataReader dataReader = prikaz.ExecuteReader();
while (dataReader.Read()) // dokud neprojdeme vsechny zaznamy
{
    Console.WriteLine("{0} {1} {2}",
        dataReader[0],                      // index sloupce (Id)
        dataReader["Czech"],                    // nazev sloupce
        dataReader.GetString(2));           // index sloupce (English) s prevedenim na pozadovany datovy typ
}

Výsledek:

Výpis řádků z databáze v C# .NET

SQL dotaz místo COUNT nyní obsahuje výčet sloupců, které vybíráme. Reader čte řádek po řádku co databáze vrátila a výsledky vypisuje do konzole.

Předávání parametrů

Udělejme z aplikace opravdový slovníček a nechme uživatele zadat slovíčko, které mu následně přeložíme.

SQL injekce

Nejprve si ukažme, jak se to nemá dělat. Naivně bychom si mohli nechat zadat slovíčko a to potom přímo vložit jako vyhledávanou frázi do dotazu. Zdrojový kód aplikace by vypadal takto:

// tento zdrojový kód je nebezpečný
string connectionString = @"Data Source=localhost\MSSQLSERVER2008;Initial Catalog=SlovnicekDB;Integrated Security=True";
using (SqlConnection pripojeni = new SqlConnection(connectionString))
{
        pripojeni.Open();

        Console.WriteLine("Zadej anglické slovíčko k překladu");
        string slovo = Console.ReadLine();

        SqlCommand prikaz = new SqlCommand("SELECT Czech FROM Word WHERE English='" + slovo + "'", pripojeni);

        SqlDataReader dataReader = prikaz.ExecuteReader();
        while (dataReader.Read()) // dokud neprojdeme vsechny zaznamy
        {
                Console.WriteLine("Překlad: {0}", dataReader["Czech"]);
        }
}
Console.ReadKey();

SQL dotaz je podobný tomu předchozímu. Nezajímají nás však již všechny řádky, ale jen ty, kde má sloupec English určitou hodnotu. Podmínku v SQL zapíšeme pomocí klauzule WHERE.

Ačkoli se zdá, že aplikace funguje korektně:

Vyhledávání v databází v C# ADO .NET

Zamyslete se nad tím, co se stane, když nějaký uživatel zadá k překladu tento řetězec:

'; DROP TABLE Word --

Škodlivý kód se nám vloží přímo do dotazu a spustí se nad databází. Útočníkovi tak dáváme plnou kontrolu and našimi daty, v tomto případě nám nenávratně vymaže celou tabulku. To je ještě poměrně nevinný útok, mohl by nám vzít i hesla uživatelů a podobně.

Předávání parametrů

Bezpečnostní problém způsobuje samozřejmě přímé vkládání hodnot do textu SQL dotazu. Útok se proto nazývá SQL injection, jako vložení cizího SQL kódu do našeho. Platí zásada, že musíme počítat s tím, že škodlivý kód může být v každém parametru, který do dotazu vkládáme. Nelze se spolehnout na to, že tato proměnné asi nebude nic od uživatele obsahovat. Aplikace se mění a do proměnné by se mohla časem dostat i třeba jen část hodnoty, kterou zadal uživatel. A věřte mi, že uživatelé jsou vynalézaví a budou vaši aplikaci zkoušet a budou vám tam tyto hodnoty vkládat.

V minulosti se parametry ošetřovaly speciální funkcí, která tzv. zescapovala škodlivé znaky. Moderní dotazy se píší pomocí tzv. Prepared Statements. Ty fungují tak, že se do dotazu místo parametrů vloží jen speciální značky. Parametry se poté předávají odděleně. Ukažme si, jak do dotazu vložit parametry správně:

string connectionString = @"Data Source=localhost\MSSQLSERVER2008;Initial Catalog=SlovnicekDB;Integrated Security=True";
using (SqlConnection pripojeni = new SqlConnection(connectionString))
{
        pripojeni.Open();

        Console.WriteLine("Zadej anglické slovíčko k překladu");
        string slovo = Console.ReadLine();

        SqlCommand prikaz = new SqlCommand("SELECT Czech FROM Word WHERE English=@slovo", pripojeni);
        prikaz.Parameters.AddWithValue("@slovo", slovo);

        SqlDataReader dataReader = prikaz.ExecuteReader();
        while (dataReader.Read()) // dokud neprojdeme vsechny zaznamy
        {
                Console.WriteLine("Překlad: {0}", dataReader["Czech"]);
        }

}
Console.ReadKey();

Všimněte si, že v SQL dotazu je jen zástupná značka, která se označuje zavináčem a libovolným názvem. Nepíšeme kolem ní ani apostrofy, ty jsou dodány později podle typu značky. Před zavoláním metody ExecuteReader() do dotazu připojíme parametry, v našem případě parametr @slovo, jehož hodnotou bude proměnná slovo. Databáze si sama parametry ošetří a nemusíme se bát, že by nám aplikaci někdo mohl nabourat.

Příště si ukážeme, jak záznamy v databázi přidávat, mazat a editovat. Zdrojové kódy dnešního programu jsou jako vždy v příloze ke stažení.


 

Stáhnout

Staženo 256x (34.77 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

  Aktivity (1)

Článek pro vás napsal JOF
Avatar

Jak se ti líbí článek?
Celkem (5 hlasů) :
55555


 



 

 

Komentáře
Zobrazit starší komentáře (3)

Avatar
parallave
Redaktor
Avatar
parallave:

Ještě přidej na konec řetězce znak "\n", znamená to další řádek. viz http://msdn.microsoft.com/…21280bw.aspx

Odpovědět 24.4.2014 15:28
C++ & C#
Avatar
Maros2470
Člen
Avatar
Odpovídá na parallave
Maros2470:

Díky za radu. Jen ještě jeden dotaz, jak to udělat s Combobox, tam se mi to také řadí za sebou? Díky za odpověď.

 
Odpovědět 24.4.2014 17:25
Avatar
Odpovídá na Maros2470
Michal Štěpánek:

Combobox musíš plnit buď jako Add items, nebo mu musíš dát jako zdroj dat výsledek toho SQL dotazu...

Odpovědět 24.4.2014 17:30
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Maros2470
Člen
Avatar
Odpovídá na Michal Štěpánek
Maros2470:

Asi jsem blbej, ale nechápu ten výsledek SQL dotazu, já myslel, že ho tam mám.
comboBox2.Text = dataReader ["Banka"].ToS­tring();, nebo ne?

 
Odpovědět 24.4.2014 17:41
Avatar
Odpovídá na Maros2470
Michal Štěpánek:

ale combobox používá vlastnosti value a item a NE Text, následně se pak odkazuješ na SelectedItem, SelectedValue nebo SelectedIndex

Odpovědět 24.4.2014 17:57
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Odpovídá na Maros2470
Michal Štěpánek:

Nevím jak to udělat v C#, ale ve VB.NET je to např. takto

//Naplnění Comboboxu CB_Zakazky - název firma a město
        Dim dt As DataTable = New DataTable     //tabulka s daty v paměti
        oledbcon.Open() //připojovací řetězec
        Dim strSql As OleDbCommand
        strSql = New OleDbCommand("SELECT Id, CisloZakazky + ' - ' + NazevZakazky AS Zakazka FROM TabulkaZakazky", oledbcon)
        Dim ada As New OleDbDataAdapter(strSql)
        ada.Fill(dt)

        With Me.CB_Zakazky
            .DataSource = dt
            .DisplayMember = "Zakazka"
            .ValueMember = "Id"
        End With
        oledbcon.Close()
Editováno 24.4.2014 18:03
Odpovědět 24.4.2014 18:02
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
JOF
Tým ITnetwork
Avatar
Odpovídá na Maros2470
JOF:

Jak píše čočkin - u comboboxu nepoužívej vlastnost Text, ale Items apod.
Např. comboBox1.Item­s.Add(dataRea­der["Kod"].ToS­tring());
(Jinak vřele doporučuji komponenty pořádně pojmenovávat a nenechávat jméno comboBox1, comboBox2 atd.)

Co se týká připojení comboboxu na nějaký datový zdroj, tak to je možné u odpojeného přístupu ...

 
Odpovědět  +1 24.4.2014 22:45
Avatar
Odpovídá na JOF
Michal Štěpánek:

Ano, připojení ComboBoxu na datový zdroj se dělá u odpojeného přístupu, ale myslím si, že stálé připojení na databázi je k ničemu, jen to zbytečně zvyšuje nároky na aplikaci, potažmo na síť, nehledě k tomu, že když aplikaci využívá více lidí najednou, je pak jednodušší ohlídat, aby si vzájemně "nepřekáželi". Spojení do DB by mělo být otevřeno jen po dobu nezbytně nutnou k provedení příkazu, jinak by mělo být zavřeno.

Odpovědět 25.4.2014 8:11
Nikdy neříkej nahlas, že to nejde. Vždycky se totiž najde blbec, který to neví a udělá to...
Avatar
Marian Benčat
Redaktor
Avatar
Odpovídá na Michal Štěpánek
Marian Benčat:

Připojení do DB se ale ihned po ukončení usingu (případně close()) neukončuje. .NET si vnitřně všechny SQLConnection pooli a v okamžiku, kdy se zavolá Close() tak ho pouze navrátí zpět do connection poolu a ještě ho nějakou dobu nechává otevřené a pak ho recykluje. Stejný connection pool pak zjistí na základě connection stringu. Proto není také nutné nad SQL connection dělat vlastní pooling a ničemu nevadí na každý dotaz dělat new SQLConnection().. tedy ,,ničemu".

 
Odpovědět 26.12.2015 17:41
Avatar
Marian Benčat
Redaktor
Avatar
Odpovídá na Marian Benčat
Marian Benčat:

Je ale samozřejmě dobré tam ten connection ihned vracet, jakmile se daná operace dokončí.

 
Odpovědět 26.12.2015 17:51
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 13. Zobrazit vše