ADO .NET 1 Model dostępu do danych za pomocą ADO.NET Strona ASP.NET ADO.NET OLE DB ODBC SQL Jet itd. 2 ADO.NET – ogólna architektura ADO.NET (ang. ActiveX Data Objects) jest to technologia umożliwiająca dostęp do danych w środowisku .NET, pozwalająca na korzystanie z dowolnego źródła danych bez konieczności ciągłego połączenia z serwerem.. ADO.NET pracuje w trybie bezpołączeniowym, to znaczy nawiązuje połączenie z serwerem i kopiuje dane na lokalny komputer, po czym zamyka to połączenie. Aplikacja może, przez dowolny okres czasu, wprowadzać modyfikacje danych; w momencie, gdy będziemy chcieli zapisać zmiany, ADO.NET nawiąże ponownie połączenie z serwerem i zaktualizuje dane przechowywane w bazie. Źródłem danych może być dowolny magazyn udostępniający dane przez interfejs ODBC lub OLE DB np.: MS SQL Server, 3 Oracle, plik XML itp. Ogólny model dostępu do danych 4 ADO.NET – warstwy dostępu do danych W aplikacjach korzystających z ADO.NET, pomiędzy aplikacją a bazą danych jest kilka warstw pośredniczących (dostawcy danych, sterowniki, interfejsy). ADO.NET składa się z dwóch podstawowych części: – klasy DataSet i innych związanych z nią klas, – z zarządzanych dostawców danych (Framework Data Provider), ułatwiających komunikację ze źródłami danych. 5 ADO.NET – ogólna architektura cd. 6 ADO.NET – dostawcy usług dostępu do danych • Dostawca danych to zestaw klas pozwalających na korzystanie z określonego źródła danych. Wśród nich znajdują się cztery podstawowe klasy: Connection - umożliwia nawiązanie połączenia z określonym źródłem danych. Command - wywołuje polecenie na źródle danych; udostępnia kolekcję parametrów (Parameters) i zawsze działa w kontekście otwartego połączenia (Connection) DataAdapter - najbliżej warstwy aplikacji; pobiera dane z określonego źródła i przekazuje je aplikacji w określonej formie; wypełnia obiekt DataSet danymi pochodzącymi ze źródła oraz umożliwia aktualizacje 7 danych w źródle na podstawie DataSet; ADO.NET – dostawcy usług dostępu do danych DataReader - udostępnia jednokierunkowy strumień danych ze źródła, w trybie „tylko do odczytu”. Może zawierać więcej niż jeden zestaw rekordów - w takim przypadku korzysta się z nich kolejno • ADO.NET komunikuje się z bazą za pośrednictwem tzw. Managed Provider – czyli interfejsu dostępowego do konkretnej bazy danych. • W zależności od projektowanej aplikacji, użycie konkretnego źródła danych może znacząco wpłynąć na jej jakość. • Wybór dostawcy dostępu do danych jest uzależniony od 8 rodzaju baz danych jakich używa dana aplikacja. ADO.NET – dostawcy usług dostępu do danych • Dostawca .NET dla OLE DB (.NET Framework Data Provider for OLE DB) - uzyskuje dostęp do danych za pośrednictwem interfejsu OLE DB. jest dostawcą nadzorowanym, gdyż komunikuje się z interfejsem OLE DB za pośrednictwem klas opakowujących. może korzystać z dowolnej bazy, dla której istnieje sterownik OLE DB (Access, Oracle, SQL Server). klasa realizująca funkcje dostawcy .NET dla OLE DB znajduje się w przestrzeni nazw System.Data.OleDb. stosowany jest dla aplikacji pracujących z wersją 9 Microsoft SQL Server 6.5 i wcześniejszych ADO.NET – dostawcy usług dostępu do danych Dostawca .NET dla SQL Servera (.NET Framework Provider for SQL Server) charakteryzuje się znacznie lepszą wydajnością niż dostawca ogólnego przeznaczenia, jakim jest OLE DB. nie korzysta z ODBC w czasie połączenia z bazą, dzięki czemu między kodem aplikacji a bazą jest o jedną warstwę mniej. klasa realizująca funkcje dostawcy .NET dla SQL Servera znajduje się w przestrzeni nazw System.Data.SqlClient. stosowany jest dla aplikacji działających z Microsoft SQL Server 7.0 i wersjach wyższych 10 ADO.NET – dostawcy usług dostępu do danych Dostawca .NET dla ODBC (.NET Framework Data Provider for ODBC) - dla aplikacji używających ODBC. Dostawca .NET dla Oracla (.NET Framework Data Provider for Oracle) - dla Oracle w wersji 8.1 i wyższych. Środowisko ADO.NET zbudowane jest wokół obiektu DataSet. 11 Model klas DataSet • Obiekt DataSet jest prostym rezydentnym magazynem danych umożliwiającym przechowywanie dowolnych informacji (np.: części bazy danych) w oderwaniu od źródła. Jest to swego rodzaju lokalna kopia bazy danych lub jej fragmentu. Struktura obiektu DataSet przypomina strukturę bazy danych. Dane w obiekcie DataSet przechowywane są w postaci tabel z danymi, składających się z wierszy i kolumn , Obiekt DataSet pozwala na definiowanie związków pomiędzy tabelami oraz nakładania ograniczeń na dane, a także umożliwia przeszukiwanie i wybieranie określonych rekordów Obiekt DataSet składa się z klas: DataTable, DataRow, 12 DataColumn, DataRelation. Ogólny schemat DataSet DataSet ExtendedProperties RelationsCollection TableCollection DataTable ChildRelations Columns DataColumns Constraints DefaultView ExtendedProperties ParentRelations PrimaryKeys Rows DataRows 13 Model klas DataSet Obiekt DataSet reprezentuje bufor danych w pamięci i jest podstawowym obiektem przekazywanym pomiędzy warstwą środkową a aplikacją kliencką. Obiekt DataSet obok tabelarycznego widoku danych zorganizowanych w postaci wierszy i kolumn, może przekształcać dane z języka i na język XML. Aplikacja wykorzystująca obiekt DataSet, ma możliwość sortowania, stronicowania, i filtrowania danych, bez potrzeby ich aktualizacji. Klasa DataSet posiada zdefiniowaną właściwość Tables, której wartością jest kolekcja obiektów DataTable (są to tabele zawarte w obiekcie DataSet). 14 Budowa obiektu DataSet 15 Właściwości obiektu DataTable Własciwość Opis Jeżeli jej wartością jest True, to w czasie porównywania napisów CaseSensitve przechowywanych w tabeli rozróżniane są małe i duże litery. HasErrors PrimaryKey Jeżeli jej wartością jest True, to oznacza, że przynajmniej jeden element tabeli zawiera nieprawidłową wartość. Wskazanie, która kolumna (lub kolumny)ma być kluczem głównym lub odczytanie klucz głównego TableName Pobranie lub ustawienie nazwy głównej tabeli. DefaultView Pobranie lub ustawienie obiektu DataView, definiującego widok tabeli. DataSet Pobranie (tylko do odczytu) właściwości obiektu DataSet, do którego należy dana tabela. 16 Metody obiektu DataTable Metoda AcceptChanges NewRow Opis Zatwierdza wszystkie zmiany dokonane w tabeli. Dodaje nowy wiersz do tabeli. RejectChanges Odrzuca wszystkie zmiany dokonane w tabeli. GetChanges Clone Wyniku jej działania otrzymujemy obiekt DataTable zawierający elementy, w których nastąpiły zmiany. Tworzy kopie obiektu ( struktura, ograniczenia), która nie zawiera danych obiektu oryginalnego. Copy Tworzy kopie obiektu (dane, struktura). Clear Usuwa wszystkie dane z tabeli. ImportRow Dodaje wiersz reprezentowany przez obiekt DataRow. GetErrors Wynikiem jest tablica obiektów DataRow zawierających błędy. Select Wynikiem działania jest tabela zawierająca elementy spełniające warunki określone przez argumenty. 17 Zdarzenia obiektu DataTable Dla obiektu DataTable możliwe jest wystąpienie następujących zdarzeń: RowChanged – zgłaszane, gdy wiersz został zmieniony. ColumnChanged – zgłaszane, gdy zmieniono wartość w kolumnie. RowDeleted – zgłaszane, gdy wiersz został usunięty. 18 Kolekcja DataRow Klasa DataTable posiada zdefiniowaną właściwość Row, której wartością jest kolekcja obiektów DataRow (są to wiersze zawarte w obiekcie DataTable). Obiekt DataRow reprezentuje jeden wiersz tabeli, przez co umożliwia dodawanie, usuwanie i modyfikację danych, przechowywanych w tabeli, w ograniczeniu tylko do jednego elementu. 19 Właściwości obiektu DataRow Właściwości HasErrors Item RowError RowState Table Opis Jeżeli jej wartością jest True, to oznacza, że przynajmniej jeden element wiersza zawiera nieprawidłową wartość. Pobranie lub ustawienie elementu (wskazanego przez argument) w tym wierszu. Pobranie lub ustawienie komunikatu o błędzie danego wiersza. Określa aktualny stan wiersza (czy zmodyfikowany, usunięty, dodany). Pobranie (tylko do odczytu) właściwości obiektu DataTable, do którego należy dany wiersz. 20 Metody obiektu DataRow Metoda Opis AcceptChanges Zatwierdza wszystkie zmiany wprowadzone w wierszu. ClearErrors Usuwa błędy ustawione dla danego wiersza. Delete ToString RejectChanges Usuwa wiersz tabeli Przedstawia obiekt w postaci napisu Odrzuca wszystkie zmiany, które wystąpiły w danym wierszu. GetType GetColumnError Tworzy obiekt z informacjami o typie obiektu Wynikiem działania jest komunikat o błędzie kolumny skojarzonej z danym argumentem. 21 Kolekcja DataColumn DataTable posiada zdefiniowaną właściwość Columns, której wartością jest kolekcja obiektów DataColumn (są to kolumny zawarte w obiekcie DataTable). Obiekt DataColumn reprezentuje strukturę obiektu DataTable (każda kolumna określa określony typ danych, jakie mogą się w niej znajdować (format i ograniczenia tabeli). 22 Właściwości obiektu DataColumn Właściwości Opis Określa czy w kolumnie może wystąpić wartość pusta, Null. AllowDBNull AutoIncrement Określa czy wartość w kolumnie, podczas dodawania nowych wierszy jest automatycznie zwiększana. Caption ColumnName DataType DefaultValue Pobranie lub ustawienie wyświetlanej nazwy kolumny. Pobranie lub ustawienie nazwy kolumny. Pobranie lub ustawienie, jakiego typu dane mogą znaleźć się w kolumnie. Pobranie lub ustawienie wartości domyślnej, jaka znajduje się w kolumnie podczas dodawania nowych wierszy. MaxLength Pobranie lub ustawienie maksymalnej długości napisu w kolumnie, (jeżeli kolumna zawiera napisy) ReadOnly Pobranie lub ustawienie wartości określającej czy wartości w danej kolumnie są tylko do odczytu. Table Pobranie (tylko do odczytu) właściwości obiektu DataTable, do którego należy dana kolumna. Unique Pobranie lub ustawienie wartości wskazującej czy dane zawarte w danej kolumnie maja charakter unikalny (nie mogą się powtarzać). 23 Metody obiektu DataColumn Metoda Equals GetType Opis Określa czy obiekt jest równy obiektowi przekazanemu w argumencie. Tworzy obiekt klasy Type, zawierający informacje o typie obiektu ToString Przedstawia obiekt w postaci napisu 24 Wielowersyjność w obiekcie DataSet Obiekt DataSet przechowuje dwie wersje wszystkich danych, a w czasie edycji wiersza, po wykonaniu metody DataRow.BeginEdit tworzona jest trzecia kopia, zawierająca wszystkie zmiany: zmiany zatwierdzane są metodą DataRow.EndEdit zmiany odrzucane są metodą DataRow.CancelEdit. Dane, którymi napełniono obiekt DataSet stanowią wersję oryginalną i służą do stworzenia wersji bieżącej, na której wykonywane są modyfikacje. Przy wykonaniu metody DataSet.AcceptChanges dane z wersji bieżącej przenoszone są do wersji oryginalnej, lub zmiany zostają odrzucone przez wykonanie metody 25 DataSet.RejectChanges. Relacje w obiekcie DataSet 26 Relacje w obiekcie DataSet Związki pomiędzy tabelami w obiekcie DataSet reprezentowane są przez kolekcję obiektów Relations. Przykładowo, definiowanie związku pomiędzy tabelami: tabela1 i tabela2 w obiekcie DataSet o nazwie ds w języku C# ma postać: ds.Relations.Add("Relacja", ds.Tables("tabela1").Columns("AdresID"), ds.Tables("tabela2").Columns("AdresID"); Najczęstszą metodą tworzenia obiektu DataSet jest wczytanie jego struktury z pliku XML. 27 Tworzenie obiektu DataSet w kodzie programu W takim wypadku, należy tworzyć obiekt DataSet w kodzie programu, co umożliwia zdefiniowanie jego struktury oraz typu danych, jakich będą używać poszczególne kolumny. Przykładowy kod w języku C#, tworzący obiekt DataSet (ds) o nazwie „Baza”, który będzie zawierał jedną tabelę (dt) o nazwie „dane”. Tabela zawiera dwa wiersze (dr) zawierające zdefiniowane dane. System.Data.DataSet ds = new DataSet("Baza"); System.Data.DataTable dt = new DataTable("dane"); System.Data.DataRow dr; 28 Tworzenie obiektu DataSet w kodzie programu //Zdefiniowanie struktury tabeli(Columns) i typów wykorzystywanych elementów dt.Columns.Add("ID", Type.GetType("System.Int32")); dt.Columns.Add("Imie" ,Type.GetType("System.String")); dt.Columns.Add("Nazwisko" ,Type.GetType("System.String")); dt.Columns.Add("Adres", Type.GetType("System.String")); //Utworzenie pierwszego wiersza i przypisanie wartości poszczególnym elementom dr=dt.NewRow(); dr["ID"]=1; dr["Imie"] = "Szczepan"; 29 Tworzenie obiektu DataSet w kodzie programu dr["Nazwisko"]="Wozniak"; dr["Adres"] = "Kaszew"; dt.Rows.Add(dr);//dodanie wiersza do tabeli dt //Utworzenie drugiego wiersza i przypisanie wartości poszczególnym elementom dr = dt.NewRow(); dr["ID"]= 2; dr["Imie"] = "Rafał"; dr["Nazwisko"]="Leśniak"; dr["Adres"] = "Turek"; dt.Rows.Add(dr); //dodanie wiersza do tabeli dt ds.Tables.Add(dt); //Zapisanie tabeli w obiekcie DataSet 30 Właściwości klasy DataSet CaseSensitive Właściwość określa czy przy porównywaniu łańcuchów znaków w tym obiekcie DataSet będzie uwzględniana wielkość liter. DataSetName nazwa danego obiektu DataSet. DefaultViewManager Zwraca obiekt klasy DataViewManager zawierający informacje o zmodyfikowanym sposobie prezentacji danego obiektu DataSet. EnforceConstraints Właściwość określa czy przy aktualizacji danych mają być wymuszane reguły. ExtendedProperties Obiekt PropertyCollection zawierający informacje określane przez użytkownika. 31 Właściwości klasy DataSet HasErrors Locale Prefix Informuje czy dane zapisane w którymkolwiek z wierszy przechowywanych w danym obiekcie DataSet zawierają błędy. Informacje lokalne używane przy porównywaniu łańcuchów znaków. Właściwość ta zwraca obiekt klasy CultureInfo. Synonim XML określający przestrzeń nazw danego obiektu DataSet. Relations Obiekt klasy DataRelationCollection reprezentujący wszystkie relacje występujące pomiędzy tabelami przechowywanym w danym obiekcie DataSet. 32 Właściwości klasy DataSet Site Zwraca interfejs ISite do obiektu klasy DataSet (służący do wiązania komponentów z obiektami zawierającymi dane). Tables Obiekt klasy DataTableCollection reprezentujący wszystkie tabele przechowywane w danym obiekcie DataSet. Xml Dane przechowywane w danym obiekcie DataSet, zapisane w formacie XML. XmlSchema Schemat XML wykorzystywany w danym obiekcie DataSet. 33 Właściwości klasy DataSet AcceptChanges Zatwierdza wszelkie modyfikacje wprowadzone w danym obiekcie DataSet. BeginInit Rozpoczyna inicjalizację obiektu DataSet w trakcie działania programu. Clear Usuwa wszystkie wiersze ze wszystkich tabel zapisanych w danym obiekcie DataSet. Clone Tworzy obiekt DataSet stanowiący dokładną kopię danego obiektu, lecz bez danych. Copy Tworzy obiekt DataSet stanowiący dokładną kopię danego obiektu i zawierający wszystkie dane. 34 Właściwości klasy DataSet EndInit GetChanges HasChanges InferXmlSchema Marge(DataSet) Zakańcza proces inicjalizacji obiektu DataSet w trakcie działania programu. Tworzy nowy obiekt klasy DataSet zawierający wyłącznie dane, które zostały zmodyfikowane. Określa czy informacje w danym obiekcie DataSet uległy jakimkolwiek zmianom. Tworzy strukturę danych na podstawie źródła danych XML. Metoda przeciążona, więcej informacji na jej temat można znaleźć w dokumentacji .NET SDK. Łączy dany obiekt DataSet z obiektem podanym jako argument wywołania metody. 35 Właściwości klasy DataSet ReadXmlSchema Tworzy strukturę danych na podstawie schematu XML. RejectChanges Odtwarza wszelkie zmiany jakie zostały wprowadzone w obiekcie DataSet. ResetRelations Przywraca domyślną wartość właściwości Relations. ResetTables Przywraca domyślną wartość właściwości Tables. ShouldSerializeRelations Informuje czy wartość właściwości Relations powinna zostać zachowana. ShouldSerializeTables Informuje czy wartość właściwości Tables powinna zostać zachowana. 36 Właściwości klasy DataSet WriteXml Zapisuje zawartość obiektu DataSet w formacie XML. WriteXmlSchema Zapisuje strukturę obiektu DataSet w formacie XML. 37 Zdarzenia klasy DataSet MergeFailed Zdarzenie jest generowane gdy zarówno źródłowy jak i docelowy obiekt DataSet będzie miał tę samą wartość klucza głównego, a jednocześnie właściwość EnforceConstraints będzie miała wartość true. PropertyChanged Zdarzenie jest generowane w razie modyfikacji którejś z właściwości kolumny. Argumentem wywołania procedury obsługi tego zdarzenia jest obiekt klasy PropertyChangedEventArgs, zawierający następującą właściwość: PropertyName — Nazwa właściwości która uległa zmianie. 38 Połączenie ze źródłem danych W ADO.NET połączenie ze źródłem danych realizowane jest to za pomocą klas: DataAdapter, Connection, Command i DataReader, zdefiniowanych dla OLEDB oraz SQL Servera 39 Połączenie ze źródłem danych Połączenie między obiektem DataSet a źródłem danych umożliwia obiekt DataAdapter. W przypadku, gdy źródłem danych jest dokument XML lub, gdy obiekt DataSet budujemy w kodzie programu obiekt DataAdapter nie jest potrzebny. Obiekt DataAdapter posiada dwie główne metody, są to: Fill – nawiązuje połączenie z bazą danych, wysyła żądanie odczytu ze źródła i umieszcza otrzymane dane w obiekcie DataSet. Update – sprawdza, jakie zmiany nastąpiły w obiekcie DataSet i przesyła elementy, które zostały zmodyfikowane do źródła danych. 40 Połączenie ze źródłem danych Metody zawarte w dostawcy: DataAdapter dostępne są dla OLEDB w klasie System.Data.OleDb.OleDbDataAdapter SQL Server w klasie System.Data.SqlClient.SqlDataAdapter 41 Model obiektu DataAdapter DataAdapter DeleteCommand InsertCommand SelectCommand UpdateCommand TableMappings ColumnMappings Metody Command, obiektu DataAdapter służą do modyfikacji danych w obiekcie DataSet. Kolekcja TableMappings określa sposób odwzorowania w 42 obiektach DataSet tabel i kolumn ze źródła danych. Metody obiektu DataAdapter Metoda Opis Fill Dodaje lub modyfikuje wiersze w obiekcie DataSet FillSchema Dodaje obiekt DataTable do obiektu DataSet i konfiguruje schemat tabeli GetFillParameters Zwraca tablicę obiektów używanych w poleceniu Select Update Aktualizuje zawartość źródła danych na podstawie informacji zapisanych w obiekcie Dataset, wykorzystując do tego właściwości DeleteCommand, InsertCommand, Update 43 Command IDataParameter Zdarzenie Opis FillError Zdarzenie zachodzi gdy podczas wykonywania metody Fill zostanie zgłoszony błąd. Argumentem jest obiekt FillErrorEventArgs. RowUpdated Zachodzi w trakcie wykonywania metody Update, po wykonaniu polecenia UPDATE. Argument obsługi jest obiektem klasy RowUpdatedEventArgs, która udostępnia właściwości: Command – zwraca obiekt Command Errors – zwraca obiekt Exception RecordsAffected – podaje ilość zmodyfikowanych wierszy StatementType – zwraca typ polecenia SQL Row – zwraca obiekt DataRow TableMapping – zwraca obiekt klasy DatatableMapping przesłany z poleceniem SQL RowUpdating Zdarzenie zachodzi podczas wykonywania metody Update, przed wykonaniem polecenia SQL UPDATE. Argument obsługi jest obiektem klasy RowUpdatedEventArgs. Klasa ta udostępnia takie 44 same właściwości jak zdarzenie RowUpdated Zdarzenia obiektu DataAdapter - przykład string sCnStr = ”northwind"; string sSQL = "select * from customers"; SqlConnection Cn = new SqlConnection(sCnStr); SqlDataAdapter DA = new SqlDataAdapter(sSQL,Cn); // utworzenie uchwytów dla zdarzeń DA.RowUpdating += new SqlRowUpdatingEventHandler(onRowUpdating); DA.RowUpdated += new SqlRowUpdatedEventHandler(onRowUpdated); DA.FillError += new FillErrorEventHandler(FillErrorHandler); DataSet Ds = new DataSet(); 45 Zdarzenia obiektu DataAdapter - przykład //wypełnienie DataSet danymi DA.Fill(Ds,”customers"); //tworzenie nowego wiersza do aktualizacji DataRow DR = Ds.Tables[”customers"].Rows[2]; //uaktualnienie wymaganego wiersza DR["PASSWORD"] = "NET"; //tworzenie obiektu do dynamicznego aktualizowania SqlCommandBuilder Cb = new SqlCommandBuilder(DA); DA.Update(Ds,”customers"); 46 Zdarzenia obiektu DataAdapter i ich wykorzystanie cd. // usunięcie uchwytów do zdarzeń DA.RowUpdating - = new SqlRowUpdatingEventHandler(onRowUpdating); DA.RowUpdated - = new SqlRowUpdatedEventHandler(onRowUpdated); DA.FillError - = new FillErrorEventHandler(FillErrorHandler); DA.Dispose(); Cn.Close(); 47 Połączenie z bazą danych Do połączenia z bazą danych służy obiekt Connection, którego zadaniem jest: nawiązanie połączenia metodą Open( ), kontrolowanie połączenia zamknięcie połączenia pomiędzy aplikacją i bazą danych metodą Close( ). Obiekt Connection jest wykorzystywany przez obiekty DataAdapter i Command w czasie realizacji połączenia. Metody obsługujące obiekt Connection dostępne są dla dostawcy: OLEDB w klasie System.Data.OleDb.OleDbConnection SQL Server w klasie 48 System.Data.SqlClient.SqlConnection Połączenie z bazą danych SQL • Podłączenie do SQL Server za pomocą .NET Framework Provider for SQL Server jest realizowane przez obiekt SqlConnection tworzący połączenie, które zostaje potem otwarte przy pomocy metody Open() SqlConnection con = new SqlConnection("server=localhost; database=Northwind;uid=sa;pwd=;"); con.Open(); • Przy tworzeniu wielu połączeń w jednej aplikacji, należy tworzyć pule połączeń, ponieważ pozwalają one zmniejszyć czas dostępu do danych jak i ruch pomiędzy bazą danych a programem z niej korzystającym. Wpływają także w znacznym stopniu na poprawę wydajności, skalowalności i 49 niezawodności aplikacji. Połączenie z bazą danych SQL • W ADO.NET pule połączeń są identyfikowane za pomocą przypisanych im danych konfiguracyjnych (ciąg znaków właściwości ConnectionString dziedziczonej po interfejsie IDbConnection przez wszystkie klasy odpowiedzialne za łączenie z bazami danych). • Podczas wywoływania metody Open() klasy SqlConnection specjalny system sprawdza na podstawie wartości właściwości ConnectionString czy istnieje już pula o podanym identyfikatorze. • Jeżeli nie ma jeszcze odpowiedniej puli to jest ona tworzona wraz z minimalną wymaganą ilością połączeń i jedno z nich jest oddawane do użytku obiektu, który zgłaszał żądanie. 50 Połączenie z bazą danych SQL • Gdy wszystkie połączenia są już zajęte, tworzone są nowe, aż do górnego limitu ustalonego dla danej puli. • Osiągnięcie maksymalnej, dozwolonej ilości wykorzystywanych połączeń w puli skutkuje ustawieniem żądania w kolejce do czasu zwolnienia jakiegoś z istniejących połączeń. • Zwolnienie połączenia i zwrócenie go do puli odbywa się po wywołaniu metody Close() lub Dispose() klasy SqlConnection. Jest ono wtedy oznaczane jako nieużywane i czeka w puli na kolejne użycie. 51 Połączenie z bazą danych SQL SqlConnection connection1 = new SqlConnection(); connection1.ConnectionString="userid=sa;datasource= \"WINDOWSXP\\SQLDB\";persist "+ "security info=False;initial catalog=northwind"; connection1.Open(); // Utworzenie pierwszej puli (A). SqlConnection connection2 = new SqlConnection(); connection2.ConnectionString="userid=sa;datasource= \"WINDOWSXP\\SQLDB\";persist "+ "security info=False;initial catalog=pub"; connection2.Open(); // Utworzenie drugiej puli (B) 52 Połączenie z innymi źródłami danych • W .NET Framework Data Provider for OLE DB połączenie uzyskuje się poprzez obiekt OleDbConnection, OleDbConnection Con = new OleDbConnection("Provider=SQLOLEDB;Data Source=localhost;" + "Integrated Security=SSPI;Initial Catalog=northwind"); nwindConn.Open(); • .NET Framework Data Provider for ODBC do tworzenia połaczeń korzysta z OdbcConnection OdbcConnection Con = new OdbcConnection("Driver={SQL Server};Server=localhost;" + "Trusted_Connection=yes;Database=northwind"); Con.Open(); 53 Połączenie z innymi źródłami danych • .NET Framework Data Provider for Oracle tworzy połączenie przy pomocy OracleConnection OracleConnection Con=newOracleConnection("Data source=MyOracleServer; Integrated Security=yes;"); nwindConn.Open(); 54 Właściwości obiektu Connection Właściwość Opis ConnectionString Łańcuch znaków służący do utworzenia połączenia ConnectionTimeout Czas jaki należy oczekiwać na połączenie z bazą danych Database Nazwa bazy danych, z którą należy korzystać po nawiązaniu połączenia DataSource Nazwa bazy danych z jaką, należy nawiązać połączenie Provider Nazwa dostawcy bazy danych State Bieżący stan połączenia 55 Metody obiektu Connection Metoda Opis BeginTransaction Rozpoczyna transakcję z bazy danych Close Zamyka połączenie z bazą danych Open Otwiera połączenie z bazą danych 56 Wykonywanie instrukcji SQL Obiekt Command umożliwia wykonanie instrukcji SQL w celu uzyskania kolekcji obiektów spełniających określone kryteria. Obiekt Command umożliwia również wykonanie instrukcji INSERT, DELETE, UPDATE i SELECT. W celu wykonania instrukcji języka SQL należy właściwości CommandText obiektu Command przypisać treść instrukcji. Metody obsługujące obiekt Command dostępne są dla dostawcy: OLEDB w klasie System.Data.OleDb.OleDbCmmand SQL Server w klasie System.Data.SqlClient.SqlCmmand 57 Model obiektu Command Command Connection Properties Parameters Parameter Properties Property 58 Wykonywanie instrukcji SQL Przykładowo, wykorzystanie obiektu Command dla wykonania instrukcji SELECT w języku C# ma postać: SqlCommand c = new SqlCommand(); // definiowanie obiektu Command c.CommandText = "SELECT*FROM Baza WHERE ID>3” // definiowanie kwerendy wybierającej z tabeli Baza // elementów, których ID>3 59 Wykonywanie instrukcji SQL • Jednym ze sposobów komunikacji z serwerem baz danych jaki udostępnia ADO.NET jest komunikacja pasywna. • Do realizacji pasywnej wymiany danych wykorzystywany jest obiekt SqlCommand, który określa parametry komendy przekazywanej serwerowi. • Obiekt ten może zadaną komendę wykonać, zwracając zbiór rekordów z bazy danych, wartość skalarną lub pusty zbiór wyników, w zależności od postaci komendy. • Zbiór rekordów będących wynikiem działania komendy SQL zostanie zwrócony dzięki metodzie ExecuteReader obiektu SqlCommand; dokładniej mówiąc, wynikiem działania tej metody będzie obiekt typu SqlDataReader, który pozwala na 60 obejrzenie wszystkich wierszy wyniku. Właściwości obiektu Command Właściwość Opis CommandText Polecenie SQL CommandType Określa sposób interpretacji polecenia SQL Connection Określa obiekt Connection Parameters Zwraca obiekt ParameterCollection, reprezentujący wszystkie parametry w poleceniu SQL Obiekt Transaction używany przez Command Transaction UpdateRowSource Liczba wierszy objętych poleceniem SQL; w przypadku poprawnego polecenia wartość 61 ta wynosi 1 Metody obiektu Command Metoda Opis Cancel Przerywa wykonanie polecenia CreateParameter Tworzy obiekt Parameter ExecuteNonQuery Wykonuje polecenie SQL, które nie zwraca żadnych danych ExecuteReader Zwraca obiekt DataReader zawierający dane uzyskane w wyniku wykonania polecenia SQL 62 Dostęp do danych Dostęp do danych zapisanych w magazynie danych umożliwia obiekt DataReader , który wyświetla je w postaci strumienia Jest to bardzo szybka metoda dostępu do danych uniemożliwiająca jednak ich modyfikację. Obiekt DataReader: w celu nawiązania połączenia z bazą danych korzysta z obiektu Connection w celu określenia, jakie dane mają być odczytane, korzysta z obiektu Command. Obiekt DataReader nie korzysta a obiektu DataSet, dane wysyłane są bezpośrednio do kontrolek umieszczonych w aplikacji. 63 Dostęp do danych SqlDataReader myReader = myCommand. ExecuteReader(); • DataReader umieszcza w pamięci jednorazowo tylko jeden wiersz danych; na żądanie tworzy strumień danych z magazynu danych. Zapobiega to występowaniu wielu problemów związanych z dostępna pamięcią, co daje w wyniku poprawę wydajności systemu. • Obiekt ten jest tylko do odczytu, nie można również powracać do rekordów, które zostały już przetworzone. • Po zapisaniu danych w obiekcie, aby przejść do kolejnych rekordów należy wywołać metodę Read() . Metoda Read obiektu DataReader powoduje przejście do następnego poprawnego rekordu (o ile taki istnieje). 64 Dostęp do danych Jedno wykonanie metody Read obiektu DataReader, zwraca jeden rekord danych. while(reader.Read()) { myString = reader.Item(0).ToString(); myInt = reader.GetInt32(1); } Metoda Read powoduje przesunięcie wewnętrznego wskaźnika danych do następnego rekordu, nie powodując rzeczywistego odczytu danych. Faktyczny odczyt ma miejsce dopiero po wykonaniu metody GetString lub GetValues. 65 Dostęp do danych Klasa DataReader musi więc posiadać, co najmniej dwa konstruktory, jeden pobierający zestaw wynikowy kwerendy drugi, który dokonuje pobrania obiektu połączenia wykorzystywanego do wykonania polecenia. Metody obsługujące obiekt DataReader dostępne są dla dostawcy: OLEDB w klasie System.Data.OleDb.OleDbDataReader SQL Server w klasie System.Data.SqlClient.SqldataReader 66 Właściwości obiektu DataReader Właściwość Opis FieldCount Ilość pól dostępnych w danym rekordzie IsClosed Określa czy obiekt został zamknięty Item Zwraca wartość wybranej kolumny RecordsAffected Okresla ilość wierszy objętych wynikami działania polecenia SQL; w przypadku poprawnego wykonania zwraca 1, w 67 przypadku błędu zwraca -1 Metoda Opis Close Zamyka obiekt DataReader GetFieldType (indeks) Zwraca obiekt reprezentujący typ danych wskazanego obiektu GetName (indeks) Zwraca nazwę kolumny GetValue (indeks) Zwraca wartość kolumny w jej oryginalnym formacie Zwraca wartość kolumny jako String GetString (indeks) IsDBNull Metoda stosowana do nieistniejących wartości przedstawiania NextResult W przypadku wykorzystania wyników wsadowego polecenia SQL, metoda ta przesuwa obiekt do następnego rekordu Read Przesuwa obiekt do następnego rekordu 68 Dostęp do danych - przykład Wykorzystanie obiektów Connection, Command oraz DataReader w celu wyświetlenia informacji o błędnych wierszach zawartych w obiekcie DataAdapter (da), ujawnionych w trakcie próby aktualizacji źródła danych (Update). klasa Connection realizuje połączenie ze źródłem danych. klasa DataReader, odczytuje elementy określone przez klasę Command (za pomocą kwerendy wybiera elementy, w których wystąpił błąd), i zapisuje je do tablicy, która następnie zostanie wyświetlona w postaci kontrolki informacyjnej. 69 Dostęp do danych - przykład try { da.Update(ds,"Baza"); //aktualizacja źródła obiektu DataAdapter } catch (DBConcurrencyException dbcex) // w przypadku błędu aktualizacji zwracany jest wyjątek { SqlCommand c = new SqlCommand(); // definiowanie obiektu Command c.CommandText = "SELECT*FROM Baza WHERE ID="+dbcex.Row["ID"].ToString(); //konfiguracja wyszukiwana konkretnego elementu w bazie danych 70 Dostęp do danych - przykład c.Connection = polaczenie; polaczenie.Open(); // definiowanie obiektu Connection // otwarcie połączenia z bazą danych SqlDataReader dr = new SqlDataReader(); // zdefiniowanie obiektu DataReader dr.Read(); // odczytanie rekordu określonego przez kwerendę Object [] rg = new Object[NR_Wiersza]; dr.GetValues(rg); dr.Close(); polaczenie.Close(); // utworzenie tablicy // zapisanie rekordu w tablicy // zamknięcie czytnika danych // zamknięcie połączenia conflict.Display(dbcex.Row, rg); } // wyświetlenie danych w kontrolce użytkownika 71 Wykonywanie operacji SQL nie zwracających danych (INSERT,UPDATE,DELETE) • Ponieważ obiekt DataReader jest tylko do odczytu, więc nie umożliwia modyfikowania danych. • Do modyfikacji danych należy użyć instrukcji języka SQL (Update,Insert,Delete ) jako parametru obiektu Command. • W celu określenia liczby rekordów, które zostały zmienione należy użyć metody ExecuteNonQuery. int wyswietl = MyCommand.ExecuteNonQuery(); 72 Wykonywanie operacji SQL nie zwracających danych (INSERT,UPDATE,DELETE) cd. Program zwraca numery wierszy dla których zostało wykonane polecenie DELETE. SqlConnection con = new SqlConnection("server=localhost; database=Northwind;uid=sa;pwd=;"); SqlCommand cmd = new SqlCommand(); cmd.CommandText = "DELETE FROM Customers WHERE CustomerName =’ZED'"; cmd.Connection = con; con.Open(); Console.WriteLine(cmd.ExecuteNonQuery().ToString()); con.Close() 73 Model przechwytywania wyjątków – klasa SQL Exception • Obiekty klasy SqlException - opisują błędy zgłaszane przez bazę danych od strony serwera. • Błędy mają następujące poziomy istotności: – 1-10 to błędy informacyjne które wskazują na pomyłki w danych otrzymanych od użytkownika, – 11-16 błędy generowane przez użytkownika które on może poprawić, – 17-25 wskazują na błędy oprogramowania lub sprzętu; błędy 17-19 umożliwiają kontynuowanie pracy natomiast przy 20 i powyżej połączenie SqlConnection zostaje zamknięte. 74 Model przechwytywania wyjątków – klasa SQL Exception Try { sqlConnection.Open(); sqlInsertCommand.ExecuteNonQuery(); sqlConnection.Close(); MessageBox.Show(”Operacja zakończona sukcesem",”OK",MessageBoxButtons.OK); } catch (SqlException blad) { foreach(SqlError err in blad.Errors) { MessageBox.Show(”Błąd: " + err.Number + " : " + err.Message); } } finally { sqlConnection.Close(); } 75 Użycie procedur składowanych, • Procedury składowane umożliwiają zawarcie w jednym rozkazie wielu operacji na bazie danych. • Przykładowa procedura bez parametrów ma postać: SqlConnection con = new SqlConnection("server=localhost; database=Northwind;uid=sa;pwd=;"); SqlCommand cmd = new SqlCommand(”Najdrozsze_produkty", con); cmd.CommandType = CommandType.StoredProcedure; con.Open(); SqlDataReader reader = cmd.ExecuteReader(); while(reader.Read()) { Console.WriteLine("{0} - {1:C}", reader.GetString(0),reader.GetDecimal(1)); } con.Close(); 76 Użycie procedur składowanych, • Przykładowa procedura z parametrami ma postać: SqlConnection con = new SqlConnection("server=localhost; database=Northwind;uid=sa;pwd=;"); SqlCommand cmd = new SqlCommand (”Najdrozsze_produkty 1", con); cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.Add(new SqlParameter("@country", SqlDbType.VarChar, 50)).Value = "USA"; cmd.Parameters.Add(new SqlParameter("@count", SqlDbType.Int)); cmd.Parameters["@count"].Direction = ParameterDirection.Output; con.Open(); SqlDataReader reader = cmd.ExecuteReader(); 77 Użycie procedur składowanych, • Przykładowa procedura z parametrami ma postać (cd) : while(reader.Read()) { Console.WriteLine("{0} - {1}", reader.GetString(0),reader.GetString(1)); } reader.Close(); Console.WriteLine("{0} -1}", "Count",cmd.Parameters["@count"].Value.ToString()); con.Close(); 78 Procedury parametryczne parametry wejściowe i wyjściowe • Procedura zwraca jako parametr wyjściowy wszystkie rekordy z tabeli Customers w których pole Country jest podane parametrem @country. • Obiekt Command umożliwia jawne definiowanie parametrów (Parameter) procedury a także dostęp do parametrów wyjściowych i zwracanych wartości. • Domyślnie parametry są traktowane jako wejściowe (Input). • Aby wyszczególnić typ parametru używa się właściwości ParameterDirection, przy pomocy której można określić czy parametry są InputOutput (wejściowe i wyjściowe), Output (wyjściowe), albo ReturnValue (zwracana 79 wartość). Pobieranie skalarów z baz danych • Do zwrotu pojedynczej wartości z bazy takiej jak np.: wynik operacji Count (*) , służy metoda ExecuteScalar obiektu Command. • Zwraca ona wartość skalarną pierwszej kolumny z pierwszego wiersza danych ze zbioru wyników. MyCommand.CommandText = "SELCT COUNT(*) FROM Customers"; int nr_c = (int)myCommand.ExecuteScalar(); 80 Transakcje w ADO.NET • Do operowania transakcjami w ADO.Net służy klasa System.Data.SqlClient.SqlTransaction. Jest to pewne novum w stosunku do poprzedniej wersji ADO gdzie obiekt transakcji był zawarty w obiekcie połączenia. • Przykład otwarcia transakcji: Private dbTemp As New SqlClient.SqlConnection Private trTemp As System.Data.SqlClient.SqlTransaction Private cmdTemp As New SqlClient.SqlCommand Private bTran As Boolean Private Sub btOtwarcie_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btOtwarcie.Click 81 Transakcje w ADO.NET • Try // obsługa wyjątków If Trim(dbTemp.ConnectionString) = "" Then dbTemp.ConnectionString = txtConnStr.Text End If If dbTemp.State = ConnectionState.Closed Then dbTemp.Open() End If If Not bTran Then // kontrola otwarcia transakcji trTemp = dbTemp.BeginTransaction() bTran = True MsgBox("Transakcja została pomyślnie otwarta!", _ MsgBoxStyle.Information) End If 82 Transakcje w ADO.NET Catch ex As Exception MsgBox(ex.ToString, MsgBoxStyle.Exclamation) End Try End Sub • Nie można otwierać wielu równoległych lub zagnieżdżonych transakcji. • W obiekcie klasy SqlTransaction nie ma żadnego mechanizmu kontrolującego stan transakcji (czy jest już otwarta, zatwierdzona itp.); do tego celu stosowana jest zmienna bTran która pozwala kontrolować otwarcie transakcji. 83 Transakcje w ADO.NET • Kod przedstawiający zatwierdzenie otwartej transakcji: Private Sub btCommTran_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles btCommTran.Click Try If Not bTran Then MsgBox("Transakcja nie została otwarta!", _ MsgBoxStyle.Exclamation) Exit Sub End If trTemp.Commit() bTran = False 84 Transakcje w ADO.NET • Kod przedstawiający zatwierdzenie otwartej transakcji (c.d.): MsgBox("Transakcja została pomyślnie zatwierdzona!", _ MsgBoxStyle.Information) Catch ex As Exception MsgBox(ex.ToString, MsgBoxStyle.Exclamation) End Try End Sub • Do wycofania transakcji wykorzystujemy metodę Rollback : Private Sub btRollTran_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles btRollTran.Click 85 Transakcje w ADO.NET • Kod przedstawiający wycofanie transakcji (c.d.): Try If Not bTran Then MsgBox("Transakcja nie jest otwarta!", _ MsgBoxStyle.Exclamation) Exit Sub End If trTemp.Rollback() bTran = False MsgBox("Transakcja została wycofana!", _ MsgBoxStyle.Information) 86 Transakcje w ADO.NET Catch ex As Exception MsgBox(ex.ToString, MsgBoxStyle.Exclamation) End Try End Sub • Pomiędzy otwarciem a zatwierdzeniem transakcji muszą być wykonane jakieś operacje: Private Sub btWykKom_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btWykKom.Click cmdTemp.Connection = dbTemp cmdTemp.Transaction = trTemp cmdTemp.CommandText = txtKom1.Text cmdTemp.ExecuteNonQuery() 87 End Sub Transakcje w ADO.NET • Transakcję należy „podłączyć” do obiektu typu SqlCommand. • W przedstawionych przykładach transakcja została sztucznie rozdzielona na poszczególne etapy; w rzeczywistości wszystko wykonuje się w ramach jednej metody, przy czym wyświetlanie komunikatów po zakończeniu poszczególnych etapów jest niedopuszczalne. • Ponieważ transakcja blokuje jedną lub wiele tabel na których wykonywane są operacje, należy przestrzegać następujących zasad: – podczas transakcji niedopuszczalna jest żadna interakcja z użytkownikiem, 88 Transakcje w ADO.NET – należy wykonywać tylko konieczne w ramach transakcji operacje, – należy starać się utrzymywać ustalony porządek w wykonywaniu operacji na wielu tabelach, co pomaga w zapobieganiu tzw. Deadlocków, czyli sytuacji kiedy dwie transakcje blokują sobie nawzajem tabele. 89 Private dbTemp As New SqlClient.SqlConnection Private trTemp As System.Data.SqlClient.SqlTransaction Private cmdTemp As New SqlClient.SqlCommand Private bTran As Boolean Private Sub btCalTran_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btCalTran.Click Try dbTemp.ConnectionString = txtConnStr.Text dbTemp.Open() trTemp = dbTemp.BeginTransaction() ‘Rozpoczęcie transakcji bTran = True cmdTemp.Connection = dbTemp cmdTemp.Transaction = trTemp cmdTemp.CommandText = txtKom1.Text 'Wykonujemy komendy 90 cmdTemp.ExecuteNonQuery() trTemp.Commit() ‘Zatwierdzenie transakcji bTran = False dbTemp.Close() MsgBox("Transakcja została wykonana pomyślnie!", _ MsgBoxStyle.Information) Catch ex As Exception If bTran Then ‘Wycofanie transakcji w przypadku błędu trTemp.Rollback() bTran = False End If MsgBox(ex.ToString, MsgBoxStyle.Exclamation) End Try End Sub 91 Transakcje w ADO.NET • Metoda Save obiektu typu SqlTransaction pozwala na zapisanie tzw. punktu zapisu (savepoint), do którego przy wykonywaniu operacji Rollback wycofujemy operacje: Private Sub btSave_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btSave.Click Try ................................... trTemp.Save("Punkt zapisu #1") 'Zapisanie punktu zapisu .................................... 'Wycofanie do punktu zapisu trTemp.Rollback("Punkt zapisu #1") 92 Transakcje w ADO.NET • Właściwość IsolationLevel. obiektu SqlTransaction pozwala ustalić sposób blokowania danych w trakcie transakcji na poziomie połączenia. Zawiera ona następujące elementy: Chaos - nie zatwierdzone zmiany z transakcji o wyższym poziomie IsolationLevel nie mogą być nadpisane, ReadCommitted - współdzielone blokowania są utrzymywane dopóki dane są czytane, przez co unika się tzw. dirty reads czyli czytania zmodyfikowanych przez inną transakcję danych których modyfikacja nie została jeszcze zatwierdzona. Ten sposób blokowania nie zapobiega jednak tzw. non-repeatable reads ani phantom data. 93 Transakcje w ADO.NET ReadUncommitted - przy tym blokowaniu dirty reads jest możliwe. Połączenie nie ustawia żadnych współdzielonych blokowań i ignoruje wyłączne blokowania (exclusive locks). RepeatableRead - ustawiane są blokowania na wszystkich danych które są używane w danej komendzie nie pozwalając na modyfikacje przez innych użytkowników. Zapobiega to tzw. non-repeatable reads czyli sytuacji kiedy transakcja czyta wielokrotnie jakieś dane i pomiędzy tymi odczytami inna transakcja zmodyfikowała te dane. Ten sposób blokowania nie zapobiega jednak tzw. phantom data. 94 Transakcje w ADO.NET Serializable - ustawiane jest na obiekcie typu DataSet zapobiegając modyfikacji i dodawania rekordu do DataSet dopóki transakcja nie jest zatwierdzona. Może zapobiec tzw. phantom data czyli sytuacji kiedy jedna transakcja modyfikuje rekordy z danego zakresu a druga transakcja wprowadza rekord do tego zakresu. Unspecified - inny IsolationLevel niż ten który jest w danej chwili używany, ale który nie może być w tym momencie ustalony. • Blokowanie danych w transakcji nie może naruszyć integralności danych. 95 Transakcje w ADO.NET • Transakcje są użytecznym elementem biblioteki ADO.Net, jednak należy z nich korzystać tylko w razie konieczności, pamiętając o ograniczeniach które ze sobą niosą. • Klasa SqlTransaction nie daje takich możliwości jak transakcje wykonywane na poziomie SQL Serwera, gdyż nie pozwala np.. na stosowanie transakcji zagnieżdżonych. • Lepsze efekty można często uzyskać realizując transakcje nie na poziomie ADO.Net, ale w procedurze wsadowej. 96 Zapis i odczyt pól BLOB • Binary large objects (BLOBs) mogą zawierać duże ilości danych co uniemożliwia zmieszczenie danych w pojedynczym wierszu który mógłby zostać przetworzony przez DataReader. • Problem ten może rozwiązać przeciążona metoda ExecuteReader przyjmująca argument CommandBehavior. SequentialAccess, która modyfikuje zachowanie obiektu DataReader tak że przyjmuje on sekwencyjne dane. • Ważna jest kolejność zwracanych pól, ponieważ SequentialAccess modyfikuje DataReader tak aby zwracać dane w kolejności, wobec tego dane raz przeczytane mogą być już niedostępne. 97 Zapis i odczyt pól BLOB SqlConnection con = new SqlConnection("Data Source = localhost;Integrated Security=SSPI; Initial Catalog = test;"); SqlCommand ob = new SqlCommand("SELECT id, obrazek FROM obrazy", con); FileStream fs; BinaryWriter bw;. // zapis obrazka do pliku (*.bmp). // ustawienie rozmiaru bufora int bufferSize = 100; // tablica wypełniana przez GetBytes byte[] outbyte = new byte[bufferSize]; 98 Zapis i odczyt pól BLOB // wartości zwrócone prze GetBytes long retval; // pozycja początkowa na wyjściu BLOB long startIndex = 0; string id = ""; con.Open(); SqlDataReader myReader =ob.ExecuteReader (CommandBehavior.SequentialAccess); while (myReader.Read()) { id = myReader.GetString(0); 99 Zapis i odczyt pól BLOB //utworzenie pliku wyjściowego. fs = new FileStream(”plik" +id + ".bmp", FileMode.OpenOrCreate, FileAccess.Write); bw = new BinaryWriter(fs); startIndex = 0; // odczyt bajtów i zachowanie ich w outbyte retval = myReader.GetBytes(1, startIndex, outbyte, 0, bufferSize); // dalszy odczyt i zapisywanie danych. while (retval == bufferSize) { bw.Write(outbyte); bw.Flush(); 100 Zapis i odczyt pól BLOB startIndex += bufferSize; retval = myReader.GetBytes(1, startIndex, outbyte, 0, bufferSize); } // zapis reszty do bufora bw.Write(outbyte, 0, (int)retval - 1); bw.Flush(); bw.Close(); // zamkniecie pliku fs.Close(); } myReader.Close(); con.Close(); 101 Pozyskiwanie informacji o strukturach danych Struktura danych włącza bazy danych lub dostępne katalogi z danych źródłowych, tabel i widoków, ograniczenia tabel w bazie danych, itd. Informacje o strukturach danych można uzyskać używając jakiegokolwiek dostawcy danych przy pomocy metody SchemaTable SqlConnection con = new SqlConnection("server=localhost; database=Northwind;uid=sa;pwd=;"); string SQL = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES " + "WHERE TABLE_TYPE = 'BASE TABLE' " + "ORDER BY TABLE_NAME"; SqlDataAdapter schemaDA = new SqlDataAdapter(SQL,con); DataTable schemaTable = new DataTable(); schemaDA.Fill(schemaTable); 102 Pobieranie danych w postaci XML • Dokument XML jest hierarchiczną bazą danych, dlatego też można wczytywać informacje z pliku (lub strumienia) XML do DataSet przy pomocy metody ReadXML(). • Metoda ta przyjmuje parametr XmlReadMode, który mówi jak mają być traktowane informacje o strukturze tabeli (Xml Schema), jeśli zawarte są one w dokumencie – dane te opisują tabele znajdujące się w pliku – np. nazwy kolumn, typy danych itp. • Obiekt DataSet posiada także możliwość zapisu danych do formatu, XML przy pomocy metody WriteXml(), której parametr określa czy chcemy zapisać wszystkie dane, czy tylko strukturę tabeli (Schema), czy tylko same dane. 103 Pobieranie danych w postaci XML cd. string xmlDoc = @"<?xml version='1.0'?> <books> <book> <title>Przykład odczytu i zapisu XML</title> </book> </books>"; // Załadowanie obiektu StringReader StringReader src = new StringReader(xmlDoc); // Tworzenie nowego obiektu DataSet i odczyt XML DataSet dset = new DataSet(); dset.ReadXml(src); // Zapis DataSet jako nowy plik XML dset.WriteXml(@"C:\dokumenty\plik.xml"); 104