70-562: Using the ADO.NET Connected Classes

Artykuł pochodzi w serii przygotowań do egzaminu 70-562 ASP.NET.

Witam w dzisiejszym artykule, w którym mowa będzie o dostępie do danych za pomocą klas, które wymagają aktywnego połączenia.

Używanie wbudowanych klas do operowania na danych

Klasy, za pomocą których możemy dodawać, edytować, usuwać itd. dane dostarczone są razem z frameworkiem .NET.
W sklad .NET’u wchodzą dostawcy do danych tacy jak: OleDb, Odbc, SQLServer, Oracl.
Poniższa tabela pokazuję listę podstawowych klas w ADO .NET. W tabeli pokazane zostały klasy bazowe, klasy klienckie oraz interfejsy:
 image

Podstawowe informacje o obiekcie DbConnection

Klasa DbConnection jest klasą abstrakcyjną, po której klasy połączenia, specyficzne dla dostawcy, dziedziczą. Poniżej hierarchia klas:
image

Oraz przykładowy kod, w którym operujemy na obiekcie DbConnection:

   1: DbConnection connection = new SqlConnection();
   2: connection.ConnectionString = "Server=.;Database=pubs;Trusted_Connection=true";
   3: connection.Open();
   4: //jakaś akcja
   5: connection.Close();

Widzimy utworzenie obiektu DbConnection a następnie wykorzystanie metody ConnectionString, która trzyma ścieżkę do naszej bazy danych. Musimy pamiętać, aby otworzyć połączenie zanim wykonamy jakiekolwiek komendy na bazie. Następnie należy połączenie zamknąć :) Możemy to zrobić za pomocą metody Close lub Dispose.

Konfigurowanie Connection String dla ODBC

Odbc jest jedną ze starszych technologii, które wspiera .NET. Poniżej najczęstsze ustawienia connection stringa dla ODBC:
Driver – sterownik ODBC dla połączenia
DSN – nazwa źródła danych, które mogą być konfigurowane przez administratora
Server – nazwa serwera, do którego się łączymy
Trusted_Connection – opis zabezpieczeń
Database – baza danych, do której się łączymy
DBQ - Zazwyczaj fizyczna ścieżka do źródła danych

Konfigurowanie Connection String dla OleDB

Podobnie jak wyżej, najczęstsze ustawienia:
 Data source - Nazwa bazy danych lub fizycznej lokalizacji pliku bazy danych
 File Name - Fizyczna lokalizacja pliku, który zawiera prawdziwy connection string.
Provider – specyficzny sterownik używający connection stringa do łączenia się danymi

Konfigurowanie Connection String dla SQL Server

Training kit przy opisywaniu connection stringa dla SQL Server wymienił bardzo dużo opcji (2,5 strony A4 :D ). Przedstawię kilka z nich:
 Data Source, addr, address, network address, server - Nazwę lub adres IP serwera bazy danych.
Initial Catalog, database – nazwa bazy danych, z której korzystamy
User ID, uid, user – nazwa użytkownika wykorzystywana do łączenia się z SQL Serverem.
 Password, pwd – hasło potrzebne do połączenia się z serwerem.
Workstation ID, wsid - Nazwa komputera klienckiego, który łączy się SQL Server.

Poza powyższymi są jeszcze między innymi: Current Language, language, Application Name, app, Packet Size, Network Library, net, network, Load Balance Timeout, connection lifetime i wiele innych. Zachęcam do doczytania na MSDN.

Dołączanie lokalnego pliku SQL do SQL EXPRESS

Aby dołączyć lokalny plik SQL do SQL Express możemy użyć do tego następującego connection stringa:

   1: Data Source=.\SQLEXPRESS;
   2: AttachDbFilename=C:\MyApplication\PUBS.MDF;
   3: Integrated Security=True;
   4: User Instance=True

 

Ustawiliśmy Data Source na SQL EXPRESS czyli tak jak chcieliśmy oraz w AttachDbFilename wskazaliśmy plik, który chcemy dołączyć.
W praktyce używa się zazwyczaj konfiguratora wbudowanego w SQL Express, który za nas wygeneruje odpowiedni connection string :)

Używanie connection string w Web Configu

Możemy używać connection stringów w pliku konfiguracyjnym www. Dzięki temu w jednym miejscu mamy kontrolę nad połączeniami do bazy co zapewnia łatwiejsze wprowadzanie zmian. Connection stringi możemy umieścić w znaczniku <connectionStrings> tak jak poniżej:

   1: <connectionStrings>
   2: <add name="PubsData" providerName="System.Data.SqlClient"
   3: connectionString="Data Source=.\SQLEXPRESS;
   4: AttachDbFilename=|DataDirectory|PUBS.MDF;
   5: Integrated Security=True; User Instance=True"/>
   6: </connectionStrings>

 

Powyższy przykład dodaje nowy connection string oraz nadaje mu nazwę PubsData. Do powyższego connection stringa możemy się dostać za pomocą klasy ConfigurationManager, tak jak w poniższym przykładzie:

   1: ConnectionStringSettings pubs = 
   2:        ConfigurationManager.ConnectionStrings["PubsData"];
   3: DbConnection connection = new SqlConnection(pubs.ConnectionString);

 

Korzystanie z Visual Studio w celu dodania nowego połączenia

Dzięki Visual Studio dodawanie nowego połączenia do danych staje się prostsze :) Możemy dodać nowe połączenie używając explorera. Otworzy nam się ładny kreator i będziemy mogli określić co chcemy zrobić. Poniżej server explorer po dodaniu nowej bazy danych. Wystarczy kliknąć na explorerze prawym przyciskiem i wybrać dodanie nowego połączenia:

image

Szyfrowanie connection stringa

Często nasze connection stringi zawierają użytkownika i hasło bazy danych. Na szęście z pomocą przychodzi nam narzędzie Aspnet_regiis.exe, dzięki któremu możemy zaszyfrować ważne dane :)
Możemy szyfrować i deszyfrować zawartość pliku Web.config przy użyciu System.Configuration. DPAPIProtectedConfigurationProvider, który korzysta z ochrony danych Windows API (DPAPI)  lub System.Configuration.RSAProtectedConfiguration- Usługodawcy, który wykorzystuje Rivest-Shamir-Adleman (RSA).
Kiedy trzeba korzystać z tego samego zaszyfrowanego pliku konfiguracji na wielu komputerach w sieci Web, należy użyć System.Configuration.RSAProtectedConfigurationProvider, który pozwala na eksport kluczy do szyfrowania danych. Klucze szyfrujące mogą być importowane do innego serwera. Jest to ustawienie domyślne. Typowy plik Web.config może wyglądać tak:

   1: <?xml version="1.0"?>
   2:   <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
   3:    <appSettings/>
   4: <connectionStrings>
   5:  <add name="ConnectionString" 
   6:    connectionString="Data Source=.\SQLEXPRESS;
   7:    AttachDbFilename=|DataDirectory|\northwnd.mdf;
   8:    Integrated Security=True;User Instance=True"
   9:    providerName="System.Data.SqlClient" />
  10: </connectionStrings>
  11: <system.web>
  12: ...
  13: </system.web>
  14: </configuration>

 

Element connectionStrings może być zaszyfrowany, jeśli uruchomimy polecenie (w konsoli Visual Studio)
z podaniem pełnej ścieżki do witryny sieci Web np:

   1: aspnet_regiis -pef "connectionStrings" "C:\...\EncryptWebSite"

Po wykonaniu polecenia nasz web config może wyglądać tak:

   1: <?xml version="1.0"?>
   2: <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
   3: <protectedData>
   4: <protectedDataSections>
   5: <add name="connectionStrings"
   6:   provider="RsaProtectedConfigurationProvider"
   7:   inheritedByChildren="false" />
   8: </protectedDataSections>
   9: </protectedData>
  10: <appSettings/>
  11: <connectionStrings>
  12: <EncryptedData Type="http://www.w3.org/2001/04/xmlenc#Element"
  13:    xmlns="http://www.w3.org/2001/04/xmlenc#">
  14: <EncryptionMethod
  15:    Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
  16: <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
  17: <EncryptedKey Recipient=""
  18:    xmlns="http://www.w3.org/2001/04/xmlenc#">
  19: <EncryptionMethod
  20:    Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
  21: <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
  22: <KeyName>Rsa Key</KeyName>
  23: </KeyInfo>
  24: <CipherData>
  25: <CipherValue>PPWA1TkWxs2i698Dj07iLUberpFYIj6wBhbmqfmNK/plarau4i1k+xq5bZzB4VJW8
  26:    OkhwzcIIdZIXff6INJ1wlZz76ZV1DIbRzbH71t6d/L/qJtuOexXxTi2LrepreK/q3svMLpsJycnDPa
  27:    t9xaGoaLq4Cg3P19Z1J6HquFILeo=</CipherValue>
  28: </CipherData>
  29: </EncryptedKey>
  30: </KeyInfo>
  31: <CipherData>
  32: <CipherValue>Q1re8ntDDv7/dHsvWbnIKdZF6COA1y3S91hmnhUN3nxYfrjSc7FrjEVyJfJhl5EDX
  33:    4kXd8ukAjrqwuBNnQbsh1PAXNFDflzB4FF+jyPKP/jm1Q9mDnmiq+NCuo3KpKj8F4vcHbcj+f3GYqq
  34:    B4pYbblAvYnjPyPrrPmxLNT9KDtDr8pDbtGnKqAfcMnQPvA8l5w3BzPM4a73Vtt2kL/z9QJRu3Svd9
  35:    33taxOO/HufRJEnE2/hcBq30WcBmEuXx3LFNjV+xVmuebrInhhxQgM2froBKYxgjwWiWNjIIjIeTI2
  36:    FQ8nZ8V8kzAVohmDYkZpCj4NQGdrjD996h97phI6NnHZYZHJ7oPRz</CipherValue>
  37: </CipherData>
  38: </EncryptedData>
  39: </connectionStrings>
  40: <system.web>
  41: ...
  42: </system.web>
  43: </configuration>

 

Do deszyfrowania możemy użyć poniższego polecenia:

   1: aspnet_regiis -pdf "connectionStrings" "C:\...\EncryptWebSite"

Korzystanie z obiektu DbCommand

DbCommand używamy do przesyłania jednej lub więcej zapytań pisanych w SQL. Może on być jednym z poniższych typów:
 Data Manipulation Language (DML) - Polecenia pobrania, wstawiania, aktualizowania lub usuwania danych
Data Definition Language (DDL) - Polecenia tworzenia tabel i innych obiektów bazy danych lub modyfikacji  schematu bazy danych.
Data Control Language (DCL) - Polecenia, które nadają, blokują, lub cofają uprawnienia

Poniższy kod pokazuje jak utworzyć i zainicjować (ale nie wykonać) DbCommand:

   1: //pobieramy connection stringa
   2: ConnectionStringSettings pubs =
   3: ConfigurationManager.ConnectionStrings["PubsData"];
   4:  
   5: DbConnection connection =
   6:  new SqlConnection(pubs.ConnectionString);
   7: //tworzymy obiekt DbCommand
   8: DbCommand cmd = connection.CreateCommand();
   9:  cmd.CommandType = CommandType.StoredProcedure;
  10:  cmd.CommandText = "uspGetCustomerById";


Ten kod tworzy obiekt SqlConnection i przypisuje go do zmiennej połączenia, która ma typ danych DbConnection. Obiekt DbConnection jest następnie wykorzystywane do tworzenia SqlCommand, który jest przypisany do zmiennej cmd. DbConnection musi być otwarty przed wywołaniem komendy. CommandText zawiera nazwę procedury przechowywanej, a CommandType wskazuje, że jest to nawiązanie do procedury składowanej.

Używanie obiektu DbParameter do przekazywania danych

Gdy zachodzi konieczność przekazania danych do procedury składowanej, należy używać obiektów DbParameter. Na przykład zdefiniowana przez użytkownika procedura składowana o nazwie uspGetCustomerById  może wymagać ID klienta w celu pobrania odpowiednich klientów. Możemy tworzyć obiekty DbParameter za pomocą Parameters.Add tak, jak poniżej:

   1: ConnectionStringSettings pubs =
   2:   ConfigurationManager.ConnectionStrings["PubsData"];
   3:  
   4: DbConnection connection =
   5:   new SqlConnection(pubs.ConnectionString);
   6: DbCommand cmd = connection.CreateCommand();
   7:  cmd.CommandType = CommandType.StoredProcedure;
   8:  cmd.CommandText = "uspGetCustomerById";
   9:  
  10: DbParameter parm = cmd.CreateParameter();
  11:  parm.ParameterName = "@Id";
  12:  parm.Value = "AROUT";
  13:  cmd.Parameters.Add(parm);

 

Używanie metody ExecuteScalar

Można wykonać zapytanie, które ma zwrócić wynik tabeli zawierającej jeden wiersz i kolumnę, takie jak kwerendy, która np. pobiera całkowitą sprzedaż z danego dzinia. W takich sytuacjach, wyniki mogą być traktowane jako jedna wartość. Na przykład, następująca instrukcja SQL zwraca wynik, który składa się z pojedynczego wiersza w jednej kolumnie:

   1: SELECT COUNT(*) FROM Sales

Poniższy kod pokazuje, jak korzystać z metody ExecuteScalar  i łatwo pobrać ilość wierszy w tabeli Sprzedaż w zmiennej o nazwie count:

   1: ConnectionStringSettings pubs =
   2:  ConfigurationManager.ConnectionStrings["PubsData"];
   3:  
   4: DbConnection connection = new SqlConnection(pubs.ConnectionString);
   5:  
   6: DbCommand cmd = connection.CreateCommand();
   7:  cmd.CommandType = CommandType.Text;
   8:  cmd.CommandText = "SELECT COUNT(*) FROM Sales";
   9:  
  10: connection.Open();
  11: int count = (int)cmd.ExecuteScalar();
  12: connection.Close();

Używanie obiektu DbDataReader

Obiekt DbDataReader dostarcza metody wysokiej wydajności do pobierania danych z magazynu danych. Dostarcza dane tylko do odczytu itp. co sprawia, że jest idealny do wypełniania kontrolek ListBox, DropDownList czy nawet GridView, które mają dane tylko do odczytu.
DbDataReader zawiera metodę Read, która pobiera dane do swojego bufora. Tylko jeden
wiersz danych jest zawsze dostępny w danym czasie. Poniższy kod wykorzystuje metodę Load obiektu DataTable w celu wypełnienia DataTable bezpośrednio z obiektu DataReader połączonego z bazą danych:

   1: ConnectionStringSettings pubs =
   2:   ConfigurationManager.ConnectionStrings["PubsData"];
   3: DbConnection connection = new SqlConnection(pubs.ConnectionString);
   4:  
   5: DbCommand cmd = connection.CreateCommand();
   6:  cmd.CommandType = CommandType.Text;
   7:  cmd.CommandText = "SELECT pub_id, pub_name FROM Publishers";
   8:  connection.Open();
   9:    DbDataReader rdr = cmd.ExecuteReader();
  10:    DataTable publishers = new DataTable();
  11:    publishers.Load(rdr, LoadOption.Upsert);
  12:  connection.Close();
  13:  
  14: GridView1.DataSource = publishers;
  15: GridView1.DataBind();

Korzystanie z DbDataAdapter

DbDataAdapter wykorzystywany jest do przetwarzania i aktualizowania danych pomiędzy DataTable i magazynem danych. Poniżej hierarchia:
image

DbDataAdapter posiada właściwość SelectCommand używaną podczas pobierania danych. SelectCommand musi zawierać prawidłowy obiekt DbCommand, który musi mieć ważne połączenie.

Korzystanie z metody Fill

Metoda Fill przenosi dane z magazynu danych do obiektu DataTable, który przekazujesz do tej metody.
Poniższy kod pokazuje jak DataTable mogą być ładowane przy użyciu metody Fill:

   1: ConnectionStringSettings pubs =
   2:  ConfigurationManager.ConnectionStrings["PubsData"];
   3:  
   4: DbConnection connection = new SqlConnection(pubs.ConnectionString);
   5: SqlCommand cmd = (SqlCommand)connection.CreateCommand();
   6:  cmd.CommandType = CommandType.Text;
   7:  cmd.CommandText = "SELECT pub_id, pub_name FROM Publishers";
   8:  
   9: SqlDataAdapter da = new SqlDataAdapter(cmd);
  10:  DataSet pubsDataSet = new DataSet("Pubs");
  11:   da.Fill(pubsDataSet, "publishers");
  12: GridView1.DataSource = pubsDataSet;

Korzystanie z klasy DbProviderFactory

Istnieje wiele powodów do pisania aplikacji, która nie wymaga kodu specyficznego dla dostawcy bazy. Firma może chcieć, np. przejść z programu Microsoft Access do SQL Server itp.
Tu z pomocą przychodzi nam tytułowa klasa. Poniżej hierarchia:
image
Klasy fabryki dostawcy są implementowane jako singletony, Na przykład poniższy kod pokazuje jak utworzyć nowe połączenie za pomocą SqlClientFactory:

   1: //Pobieramy instancję singletonu
   2: DbProviderFactory factory = SqlClientFactory.Instance;
   3: public DbConnection GetProviderConnection()
   4: {
   5:  DbConnection connection = factory.CreateConnection();
   6:   connection.ConnectionString = @"Data Source=.\SQLEXPRESS;"
   7:    + "AttachDbFilename=|DataDirectory|PUBS.MDF;"
   8:    + "Integrated Security=True;User Instance=True";
   9:    return connection;
  10: }

Używanie klasy DbProviderFactories

Aby zapytać o listę dostępnych fabryk, możesz użyć klasy DbProviderFactories. Zawiera ona metodę o nazwie GetFactoryClasses, która zwraca DataTable. Poniżej przykład pokazujący pobranie listy fabryk oraz informacji o nich i przypisanie do grid viewa:

   1: DataTable providersList = null;
   2: providersList = System.Data.Common.DbProviderFactories.GetFactoryClasses();
   3: GridView1.DataSource = providersList;
   4: GridView1.DataBind();

Używanie ADO.NET Transaction Object

Transakcja jest to atomowa jednostka pracy, która musi być wypełniona w całości. Transakcja się powiedzie, jeśli została wykona cała operacja. Transakcji ma cztery podstawowe właściwości: Cząsteczkowość, spójność, izolację i trwałość (tzw. właściwości ACID).
Możemy użyć obiektu DbConnection z metodą BeginTransaction, która tworzy obiekt DbTransaction. Dzięki temu otrzymamy możliwość obsługiwania transakcji. Poniższy kod pokazuje jak to osiągnąć:

   1: ConnectionStringSettings cnSetting = ConfigurationManager.ConnectionStrings["PubsData"];
   2: using (SqlConnection cn = new SqlConnection())
   3: {
   4:  cn.ConnectionString = cnSetting.ConnectionString;
   5:  cn.Open();
   6: using (SqlTransaction tran = cn.BeginTransaction())
   7: {
   8:  try
   9:  {
  10:   //jakis kod
  11: using (SqlCommand cmd = cn.CreateCommand())
  12: {
  13:  cmd.Transaction = tran;
  14:  cmd.CommandText = "UPDATE jobs SET min_lvl=min_lvl * 1.1";
  15:  cmd.ExecuteNonQuery();
  16: }
  17: tran.Commit();
  18: }
  19: catch (Exception xcp)
  20: {
  21:  tran.Rollback();
  22: }
  23: }
  24: }

Używanie LINQ do pracy z danymi

Linq jest to bardzo fajny mechanizm wprowadzony w .NET 3.0, który np. z naszych tabel w bazie danych tworzy obiekty :) Dzięki linq nie tylko możemy się odnosić do baz Sql Serwer ale również XML czy zwykłych obiektów.
Aby zacząć zabawę z linq musimy stworzyć jakąś bazę oraz tabelę w niej. Następnie musimy stworzyć mapowanie bazy. Aby to osiągnąć dodajemy do projektu nowy plik LinqToSql. Następnie z Serwer Explorera przeciągamy nasze tabele na forme. Wygląda to np. tak:
image

Załóżmy, że nasz plik z mapowaniem nazywał się Dane. Dzięki Linq stworzy nam się klasa która nazwana zostanie (w tym wypadku) DaneDataContext. Dzięki niej będziemy mogli odwoływać się do obiektów stworzonych przez LINQ.

Zapytania w linq, tworzenie nowych danych

Aby wyciągnąć dane z tabeli za pomocą linq tworzymy najpierw nowy obiekt DataContext, o którym wspominałem w poprzednim akapicie a następnie za pomocą zapytania podobnego do sql wyciągamy dane. Poniższy przykład pokazuje jak to osiągnąć:

   1: using (var zalacznik = new DaneLinqDataContext())
   2:              
   3:                  var zatwierdz = from zat in zalacznik.Zalaczniks where zat.NewsId == null select zat;
   4:        }

Przykład dodania danych prezentuje się następująco:

   1: using (var DodajNews = new DaneDataContext())
   2:             {
   3:  
   4:                 New news = DodajNews.News.Single(n => n.Id == id);
   5:                 news.Kategoria = int.Parse(KategorieDrop.SelectedValue);
   6:                 news.Tytul = TytulNewsBox.Text;
   7:                 news.SkroconyOpis = SkroconyOpisBox.Text;
   8:                 news.Tresc = PoleWpis.Content;
   9:                 news.Piorytet = int.Parse(PriorytetBox.SelectedValue);
  10:                 news.Waga = int.Parse(WagaBox.SelectedValue);
  11:                 news.DataUtworzenia = DateTime.Now;
  12:                 DodajNews.SubmitChanges();
  13:                          
  14:  
  15:             }

Widzimy, że wystarczy stworzyć nowy obiekt linq a następnie ustawiać właściwości danej klasy i na końcu je zapisać.

 

To wszystko w dzisiejszym artykule. Jak widzicie zakres materiału jest bardzo duży. W samym training kicie było ok 50 stron. Warto poczytać dużo więcej o powyższych zagadnieniach ponieważ przedstawione przeze mnie są tylko podstawowe informację. Nie pisałem również o asynchronicznym dostępnie do danych.
Wybaczcie literówki ale jest już troszkę późno a nie wiem czy jutro zdążę sprawdzić :)

Tagi: , , , , ,

Add comment




  Country flag
biuquote
  • Comment
  • Preview
Loading


Eastgroup.pl na facebooku