czwartek, 17 stycznia 2013

70-516 DataSet

DataSet można sobie wyobrazić jako pamięciową bazę danych. Obiekt ten nie oferuje oczywiście takich aspektów jak transakcje.
Obiekt ten przechowuje w sobie tabele jako obiekty klasy DataTable oraz relacje pomiędzy nimi przedstawione jako obiekty DataRelation.
DataSet umożliwia ponadto:
  • kopiowanie danych jak i schematu do nowych DataSet-ów
  • łączenie z innym DataSet-em
  • śledzenie i reagowanie na zmiany
DataSet można stworzyć zarówno w kodzie jak i dostarczając XML z konfiguracją.
Przykład tworzenia DataSet-a zawierającego tabele klientów i zamówień, oraz połączenie pomiędzy tymi tabelami:

Code:
            var clientDataSet = new DataSet("ClientOrders");
            
            var clientTable = new DataTable("Client");
            clientTable.Columns.Add(new DataColumn("Id", typeof (int)));
            clientTable.Columns.Add("FirstName", typeof (string));
            clientTable.Columns.Add("LastName", typeof(string));
            clientTable.Columns.Add("BirthYear", typeof(int));
            clientTable.PrimaryKey = new DataColumn[] {clientTable.Columns["Id"]};

            var orderTable = new DataTable("Order");
            orderTable.Columns.Add("Id", typeof (int));
            orderTable.Columns.Add("CreationData", typeof (DateTime));
            orderTable.Columns.Add("Price", typeof (decimal));
            orderTable.Columns.Add("ClientId", typeof (int));
            orderTable.PrimaryKey = new DataColumn[] {orderTable.Columns["Id"]};

            clientDataSet.Tables.AddRange(new DataTable[] {clientTable, orderTable});
            clientDataSet.Relations.Add("client_order", clientTable.Columns["Id"], orderTable.Columns["ClientId"]);


Lepszym rozwiązaniem od powyższego, jest stworzenie tzw. typowanego DataSet-u. Tworzymy go dziedzicząc po klasie DataSet a następnie tworząc właściwości odpowiadające poszczególnym tabelą. Zaletą tego rozwiązania jest późniejsze wsparcie IntelliSense podczas odnoszenia się do tabel.

Drugim sposobem tworzenia typowanego DataSet-a jest stworzenie pliku XML zawierającego specyfikację. XML-a można stworzyć ręcznie ale istnieje też narzędzie umożliwiające graficzne tworzenie reprezentacji DataSet-a. Aby uruchomić narzędzie wystarczy dodać do projektu nowy plik typu DataSet:







Po dodaniu nowych plików zostanie otwarty edytor gdzie w graficzny sposób stworzymy schemat:





Relacje
Za relacje pomiędzy tabelami odpowiada obiekt typu DataRelation. Za pomocą obiektu relacji możemy nawigować w obie strony: od tabeli nadrzędnej do podrzędnej jak i od podrzędnej do nadrzędnej.
Przykład nawigacji (na końcu zostanie udostępniony kod do pobrania z całością omawianego tutaj materiału):

Code:
            //1. Od tabeli nadrzędnej do podrzędnej
            var childRows = simpleDataSet.Tables["Client"].Rows[0].GetChildRows("client_order");
            foreach (var childRow in childRows)
            {
                Console.WriteLine("{0} {1} {2}", childRow["Id"], childRow["CreationDate"], childRow["Price"]);
            }
            Console.WriteLine("----------------------------");
            //2. Od podrzędnej do nadrzędnej
            var parentRow = simpleDataSet.Tables["Order"].Rows[0].GetParentRow("client_order");
            Console.WriteLine("{0} {1} {2}", parentRow["Id"], parentRow["FirstName"], parentRow["LastName"]);


Klucze obce
Podczas tworzenia relacji stworzyliśmy klucz obcy w tabeli Order. Za tworzenie klucza odpowiada jeden z parametrów funkcji Add dla kolekcji relacji:

Code:
clientDataSet.Relations.Add("client_order", clientTable.Columns["Id"], orderTable.Columns["ClientId"], true);

Domyślnie parametr ten jest ustawiony na true czyli klucz obcy jest tworzony za każdym razem.


Kaskadowe uaktualnianie i usuwanie danych
Klucze obce wymuszają zachowanie integralności pomiędzy tabelą nadrzędną a podrzędną. Wiersze podrzędne nie mogą zostać stworzone, dopóki wiersz nadrzędny nie będzie istniał. W bazach danych jak i w obiekcie DataRelation można ustawić tryb kaskadowego usuwania danych. W tym trybie, gdy zostanie usunięty wiersz z tabeli nadrzędnej, wiersze z tabeli podrzędnej zostaną usunięte automatycznie.
Typ wyliczeniowy Rule zawiera inne możliwości dla reguł usuwania jak i uaktualnia danych:
  • Cascade - domyślne ustawienie - uaktualnia lub kasuje potomne wiersze w momencie zmiany unikalnego klucza, bądź usunięcia wiersza nadrzędnego 
  • None - spowoduje rzucenie wyjątku w przypadku próby usunięcia wiersza nadrzędnego lub próby zmiany jego wartości
  • SetDefault - ustawia wartość domyślną dla kolumny klucza obcego
  • SetNull - ustawia null dla klucza obcego
Aby ustawić inną wartość dla klucza głównego, np. aby rzucał wyjątkiem, jeżeli w tabeli istnieją podrzędne wiersze, należy pobrać klucz główny i zmienić jego właściwość DeleteRule/UpdateRule:

Code:
            ((ForeignKeyConstraint)orderTable.Constraints["client_order"]).DeleteRule = Rule.None;
            ((ForeignKeyConstraint)orderTable.Constraints["client_order"]).UpdateRule = Rule.None;



Łączenie DataSet-ów
Przykładem zastosowania powyższej operacji jest pobranie z bazy danych, a następnie dodanie danych pobranych z pliku. DataSet umożliwia łączenie danych z obiektami typu DataSet, DataTable oraz DataRow za pomocą metody Merge:

Code:
            var copyOfDataset = simpleDataSet.Copy();
            copyOfDataset.Tables["Client"].Rows[2]["FirstName"] = "Kamil";
            copyOfDataset.Tables["Order"].Rows.Add(9, DateTime.Parse("2013-01-26"), 256.78, 2);

            simpleDataSet.Merge(copyOfDataset, false, MissingSchemaAction.AddWithKey);

Parametry które przyjmuje metoda Merge:
  • pierwszy parametr to DataSet z którym ma nastąpić połączenie
  • drugi parametr mówi o tym czy zmiany dokonane w drugim DataSet mają nadpisać dane w pierwszym. W podanym wyżej przykładzie wiersz gdzie zmieniamy imię na Kamil nie ma stanu Unchanged - w takim przypadku flaga ustawiona na true sprawiłaby, że dane nie zostały by uaktualnione
  • trzeci parametr to wartość typu wyliczeniowego MissingSchemaAction - ustawienie to odpowiada w jaki sposób mają zostać potraktowane nowe dane.
Typ wyliczeniowy MissingSchemaAction:
  • Add - dodaje wymagane obiekty DataTable oraz DataColumn do schematu
  • AddWithKey - dodaje tabele, kolumny oraz klucz główny
  • Error - jeżeli dana kolumna nie istnieje w DataSecie docelowym, zostanie rzucony wyjątek
  • Ignore - dane przechowywane w kolumnach, które nie występują w źródle nie zostaną dodane
Należy pamiętać o tym aby dodawać klucz główny do tabeli, którą zamierzamy łączyć z naszym DataSetem. Jeżeli tego nie zrobimy, wiersze zamiast ulec uaktualnieniu zostaną powielone (wiersze z drugiej tabeli zostaną skopiowane jako nowe rekordy).

Link do pobrania źródeł: http://sdrv.ms/ZYw9oE

Brak komentarzy:

Prześlij komentarz