Dane są wszędzie, a posiadanie ich I umiejętne wykorzystanie daje przewagę nad konkurencją. Warto więc z nich korzystać w naszych programach. W nauce programowania przychodzi taki moment, że już nie wystarczy zapisywanie pliku txt z informacją. Nawet książka Head First C# zaczyna się od programu wykorzystującego dane. Pracując jako programista nie da się uniknąć aplikacji współpracujących z różnymi bazami danych.
Microsoft wraz z .NET Framework dostarcza świetne narzędzie o nazwie ADO.NET. Jest to bogaty zbiór składników, które umożliwiają komunikację z bazami danych. Pamiętam jak stworzyłem pierwszą swoją aplikację, która połączyła się do bazy i tę radość, jak mi się to udało. Często jest też to aplikacja zaliczeniowa na studiach, więc tym bardziej wpis będzie użyteczny.
Zacznijmy od początku. Stwórzmy sobie klasę, która będzie reprezentowała tabelę w bazie danych:
public class Osoba
{
public int Id { get; set; }
public int Imie { get; set; }
public int Nazwisko { get; set; }
}
Następnie tworzymy klasę odpowiedzialną za manipulację danymi. Aby skorzystać z ADO.NET, należy dodać using do biblioteki System.Data i System.Data.SqlClient. Dodajemy konstruktor, który jako parametry będzie przyjmował adres serwera bazy danych i nazwę bazy danych. Kolejny konstruktor będzie przyjmował dodatkowo login i hasło. Pozwoli to nam tworzyć obiekt połączenia z bazą danych w zależności od tego, czy korzystamy z Windows Authentication, czy SQL Server Authentication. Musimy jeszcze dodać Connection String, który będziemy tworzyli w konstruktorze. Jest to konfiguracja połączenia do bazy danych, dzięki której ADO.NET będzie w stanie nas połączyć.
public class BazaDanych
{
private string _connectionString;
public BazaDanych(string adresSerwera, string nazwaBazyDanych)
{
_connectionString = $"Data Source={adresSerwera};Initial Catalog={nazwaBazyDanych};Integrated Security=True";
}
public BazaDanych(string adresSerwera, string nazwaBazyDanych, string login, string haslo)
{
_connectionString = $"Data Source={adresSerwera};Initial Catalog={nazwaBazyDanych};User id={login};Password={haslo}";
}
}
Teraz musimy napisać metodę, która będzie wykonywała zapytanie w bazie danych i zwracała wynik SELECTa w postaci kolekcji wierszy.
Tworzymy obiekt SqlConnection, który będzie odpowiedzialny za połączenie z bazą danych. Z racji tego, że ma on zaimplementowany interfejs IDisposible i metodę Dispose(), możemy utworzyć go korzystając z using. Jako parametr wejściowy do konstruktora przekazujemy naszego Connection Stringa.
Teraz tworzymy obiekt klasy SqlCommand, który wykona nasze zapytanie na wcześniej utworzonym połączeniu. Nadajemy mu typ tekstowy: CommandType.Text. Do konstruktora przekazujemy nasze zapytanie do bazy i obiekt odpowiedzialny za połączenie z bazą danych.
Następnie tworzymy obiekt DataSet, do którego przekażemy odebrane dane. Ma on bardzo dużo możliwości, ale zazwyczaj wykorzystuje się tylko te podstawowe (polecam poczytać trochę o tym obiekcie w Internecie).
Kolejnym krokiem jest stworzenie obiektu SqlDataAdapter, który odbierze dane z bazy. Korzystając z metody Fill na tym obiekcie i jako parametr podając wcześniej utworzony obiekt DataSet nasze dane zostają pobrane z bazy danych i „wrzucone” do obiektu dataSet.
W ostatnim kroku pobieramy z obiektu dataSet kolekcję tabel pobranych z bazy danych i zwracamy je jako wynik działania naszej metody.
private DataRowCollection ZapytanieSelect(string zapytanie)
{
DataRowCollection dr;
string constr = "";
using (SqlConnection scon = new SqlConnection(constr))
{
SqlCommand cmd = new SqlCommand(zapytanie, scon);
cmd.CommandType = CommandType.Text;
DataSet dataSet = new DataSet();
SqlDataAdapter adapter = new SqlDataAdapter(cmd);
adapter.Fill(dataSet);
dr = dataSet.Tables[0].Rows;
}
return dr;
}
Ostatnim krokiem jest wykorzystanie wyżej stworzonej metody. W tym celu piszemy metodę publiczną, która wykona zapytanie i zwróci nam kolekcję obiektów klasy Osoba.
public IEnumerable PobierzOsoby()
{
var dane = ZapytanieSelect("SELECT Id, Imie, Nazwisko FROM Osoby");
foreach(DataRow dr in dane)
{
yield return new Osoba
{
Id = int.Parse(dr["Id"].ToString()),
Imie = dr["Imie"].ToString(),
Nazwisko = dr["Nazwisko"].ToString()
};
}
}
W pierwszym kroku korzystamy z przygotowanej metody i pobieramy z bazy danych kolekcję wierszy. Teraz wystarczy zrobić pętlę foreach, która przeiteruje nam naszą kolekcję wierszy. Trzeba pamiętać, aby podać typ danych (DataRow), ponieważ korzystając z var zamiast otrzymywać obiekt DataRow otrzymamy obiekt typu ogólnego Object. W pętli tworzymy nowe obiekty z przedmiotami pobranymi z bazy danych. Można to zrobić na dwa sposoby: 1. dataRow[„NazwaKolumny”] 2. dataRow[numerKolumny] Zalecane jest stosowanie sposobu nr. 1, ponieważ przy dużej ilości pobieranych kolumn łatwo pomylić numer kolumny, dodatkowo zabezpieczamy się przed błędem związanym ze zmianą zapytania np. jeśli podamy nazwy kolumn w innej kolejności, to sposób 1 będzie nadal szukał nazw kolumn, a sposób 2 nie odniesie się do zmian i zamieniając kolejnością siłę z mocą stworzymy obiekt z błędnymi danymi. Wszystkie wyciągnięte dane z DataRow są typu Object, dlatego należy je przekonwertować na odpowiednie typy.
Gdy mamy przygotowaną już metodę, możemy z niej skorzystać i pobrać listę osób:
BazaDanych baza = new BazaDanych("SQLEXPRESS", "PrzykladowaBaza");
List osoby = baza.PobierzOsoby().ToList();
Są tu dwie niewyjaśnione rzeczy: znak $ w konstruktorach i tajemnicze yield return z którym powiązany jest interfejs IEnumerable. Są to jednak bardziej złożone tematy, więc wyjaśnię je w kolejnych wpisach.
