You are on page 1of 80

Janusz Grczyski

Wprowadzenie do programowania baz danych w VB.NET

WSZiM w Sochaczewie, 2011

Spis treci
1 WSTP DO ADO.NET .......................................................................................... 4 1.1 KOMPONENTY ADO.NET ................................................................................. 4 1.2 CONNECTION STRING ........................................................................................ 6 1.3 OBIEKT CONNECTION ........................................................................................ 7 1.3.1 SqlConnection .............................................................................................. 7 1.3.2 OleDbConnection ......................................................................................... 7 1.4 OBIEKT COMMAND............................................................................................ 7 1.4.1 Metoda ExecuteReader................................................................................. 8 1.4.2 Metoda ExecuteScalar.................................................................................. 8 1.4.3 Metoda ExecuteNonQuery............................................................................ 8 1.5 OBIEKT DATAREADER ...................................................................................... 9 1.5.1 Metoda Read................................................................................................. 9 1.5.2 Metoda NextResult...................................................................................... 10 1.5.3 Metoda GetSchemaTable............................................................................ 12 1.6 OBIEKT DATAADAPTER .................................................................................. 13 1.6.1 DataAdapter, polecenie Select ................................................................... 13 1.6.2 DataAdapter, polecenia modyfikujce........................................................ 14 1.6.3 DataAdapter i CommandBuilder................................................................ 14 1.7 PROCEDURY PRZECHOWYWANE W VB.NET ................................................... 15 1.7.1 Procedura bezargumentowa....................................................................... 15 1.7.2 Procedura z parametrami........................................................................... 17 1.8 WYKORZYSTYWANE RDA DANYCH. .......................................................... 19 1.9 STRUKTURA APLIKACJI.................................................................................... 20 1.10 DODATKOWE REFERENCJE............................................................................... 21 1.11 MODU WSPLNY............................................................................................ 23 1.12 KLASA CMOJEOLE .......................................................................................... 23 1.13 OPIS POZYCJI OLEDB W MENU ....................................................................... 29 1.13.1 Adapter ..................................................................................................... 29 1.13.2 DataReader............................................................................................... 32 1.13.3 Przegld tabel........................................................................................... 34 1.14 OPIS POZYCJI ADO (COM) W MENU ............................................................... 38 1.14.1 Pobieranie tabel i pl, test zapytania ....................................................... 38 1.14.2 Zbudowanie dynamicznego formularza.................................................... 50

1.15 OPIS POZYCJI SQL........................................................................................... 57 1.15.1 Adapter ..................................................................................................... 57 1.15.2 Update tabeli via procedura przechowywana (klasycznie) ...................... 62 1.15.3 Update tabeli via procedura przechowywana wg JG ............................... 68 1.16 KLASA CFORSTORAGESUB ............................................................................. 74 1.16.1 Deklaracja zmiennych i prywatnych procedur klasy ................................ 74 1.16.2 Kilka wybranych metod publicznych ........................................................ 77

1 Wstp do ADO.NET
ADO.NET jest opracowan przez Microsoft technologi dostpow do baz danych, jest to jeden z wanych skadnikw rodowiska .NET Framework. Technologia ta zapewnia komunikacj z relacyjnymi i nierelacyjnymi rdami danych poprzez zestaw komponentw. ADO.NET zabezpiecza zarwno model poczeniowy ze rdem danych jak i model bezpoczeniowy. W przypadku modelu poczeniowego (Connection Oriented Data Access Architecture) aplikacja zestawia poczenie ze rdem danych, a nastpnie interaktywnie wsppracuje z tym rdem wykorzystujc zapytania jzyka SQL. W tej technologii poczenie ze rdem danych cay czas pozostaje otwarte, take wtedy, gdy aplikacja nie wykonuje adnych operacji na rdle danych. W modelu bezpoczeniowym (Disconnected Data Access Architecture) poczenie jest zestawianie jedynie na moment, kiedy dane s pobierane ze rda i umieszczane w obiekcie typu DataSet lub zwracane do rda danych.

1.1 Komponenty ADO.NET


Poniej pokazany jest schemat funkcjonowania dostpu do baz danych z wykorzystaniem modelu ADO.NET (rdo: http://vb.net-informations.com).

Dwoma kluczowymi komponentami ADO.NET jest Data Provider oraz DataSet. Pierwszy z nich odpowiada za zestawienie poczenia ze rdem danych, a drugi za reprezentowanie danych pobranych ze rda (z bazy danych). rodowisko .Net Framework zabezpiecza trzy gwne rodzaje provaiderw dla modelu ADO.NET: Microsoft SQL Provider obsuguje dostp do baz danych MS SQL Server; OLEDB pozwala na dostp do innych baz danych i innych rde danych; ODBC pozwala na tworzenie aplikacji z jednoczesnym dostpem do rnych typw danych.

W przypadku zestawiania poczenia z MS SQL Server uywamy obiektu SqlConnection, do innych baz danych OleDbConnection, a w przypadku korzystania z ODBC korzystamy z obiektu OdbcConnection. Cztery obiekty wchodzce w skad komponentu DataProvider zabezpieczaj jego funkcjonalno. S to obiekty: Connection odpowiada za fizyczne zestawienie poczenie ze rdem danych; Command odpowiada za wykonanie zapytania skierowanego do rda danych, moe to by jawne polecenie SQL lub procedura przechowywana, zarwno wybierajce jak i akcyjne (modyfikujce informacje w bazie danych); DataReader wyspecjalizowany obiekt do pobierania strumienia danych typu tylko do odczytu i do przegldania jedynie w przd; DataAdapter obiekt poredniczcy w dostarczenie danych pobranych ze rda do obiektu DataSet.

Obiekt DataSet zabezpiecza bezpoczeniowy dostp do danych pobranych ze rda, jest tym samym cakowicie niezaleny od tego rda (poza momentem, gdy dane s pobierane z tego rda). Obiekt DataSet moe zawiera kolekcje obiektw DataTable, z ktrych kady jest wirtualnym odpowiednikiem danych pozyskanych ze rda danych. Inn wan kolekcj jest kolekcja relacji wica obiekty DataTable. Obiekt DataTable zawiera kolekcj wierszy, kolekcj kolumn oraz kolekcj warunkw nakadanych na poszczeglne pola. Poniej pokazany jest schemat obiektu DataSet.

1.2 Connection String


Pod tym pojciem rozumiemy informacj tekstow zawierajc dane niezbdne do zestawienia poczenia ze rdem danych. W zalenoci od uytego provaidera connection string ma nastpujc skadni: Microsoft SQL Server Connection String connetionString ="Data Source = ServerName; Initial Catalog = Databasename; User ID = UserName; Password=Password" OLEDB Data Provider Connection String connetionString = "Provider = Microsoft.Jet.OLEDB.4.0; Data Source = yourdatabasename.mdb;" ODBC Connection String connetionString = "Driver = {Microsoft Access Driver (*.mdb)}; DBQ = yourdatabasename.mdb;"

1.3 Obiekt Connection


1.3.1 SqlConnection
Obiekt SqlConnection odpowiada za utworzenie fizycznego poczenia aplikacji z baz danych serwera SQL. Instancja klasy SqlConnection otrzymuje jako argument acuch poczenia (connection string). Po zestawieniu (otwarciu) poczenia mog by wykonywane polecenia pobrania czy modyfikacji danych w rdle danych. Poniej jedna z moliwych instrukcji deklaracji i utworzenia instancji obiektu SqlConnection, kolejna otwiera poczenie z baz danych.
Dim conn As New SqlConnection(connection_string) conn.Open()

Utworzona instancja obiektu Connection nie jest automatycznie zamykana, nawet po wyjciu z procedury, w ktrej nastpio zestawienie poczenia. Musimy pamita o tym, aby po zakoczeniu operacji na bazie danych zamkn otwarte poczenie, wystarczy skorzysta z metody Close. Przypisanie wartoci Nothing jest sygnaem do zwolnienia zasobu (do jego usunicia z pamici RAM komputera).
conn.Close() conn = Nothing

1.3.2

OleDbConnection

Obiekt OleDbConnection odpowiada za utworzenie fizycznego poczenia aplikacji z baz danych wskazan w acuchu poczenia. Instancja klasy SqlConnection otrzymuje jako argument acuch poczenia (connection string). Po zestawieniu (otwarciu) poczenia mog by wykonywane polecenia pobrania czy modyfikacji danych w rdle danych. Instrukcje deklaracji, utworzenia, otwarcia i zamknicia poczenia s podobne do przedstawionych wyej, caa rnica dotyczy uycia obiektu OleDbConnection zamiast SqlConnection.

1.4 Obiekt Command


Obiekt Command odpowiada za wykonanie jawnego zapytania SQL lub procedury przechowywanej. Wymaga instancji obiektu Connection, otwarcia poczenia do rda danych, a nastpnie przypisania go do waciwoci Connection obiektu Command. Jeeli wykonane zapytanie lub procedura przechowywana zwracaj dane, to obiekt DataReader jest uywany do ich odebrania z obiektu Command. Istotne waciwoci tego obiektu to CommandText, ktry reprezentuje tekst zawierajcy polecenie do wykonania lub nazw procedury przechowywanej oraz waciwo

CommandType, ktra okrela rodzaj polecenia. Moe to by jawne polecenie SQL, procedura przechowywana, moe by to take tabela. Polecenie wykonywane jest po wywoaniu jednej z trzech metod, ktre udostpniane s przez obiekt Command.

1.4.1

Metoda ExecuteReader

Metoda ta uywana jest do pobrania ze rda danych takiego ich zestawu, ktry chcemy tylko i wycznie przeglda i to jedynie w przd. Zalet jest szybko dziaania tej metody. Wywoanie tej metody dostarcza dane do obiektu DataReader, ktrego nie mona utworzy programowo w kodzie (chodzi o to, e obiekt tego typu powstaje jako efekt wywoania metody ExecuteReader). Poniej przykad fragmentu kodu, w ktrym tworzony jest obiekt DataReader.
Dim dt As New DataTable Try conn.Open() Dim oleDBCmd As New OleDbCommand(Select * From Klienci, conn) Dim oleDbReader As OleDbDataReader = oleDBCmd.ExecuteReader dt.Load(oleDbReader)

1.4.2

Metoda ExecuteScalar

Metoda ta wykorzystywana jest w tych sytuacjach, w ktrych zapytanie SQL lub procedura przechowywana zwraca pojedynczy wynik z bazy danych. Dokadnie rzecz biorc metoda ExecuteScalar zwraca wynik z pierwszego pola pierwszego rekordu. Przykadowy fragment kodu wykorzystujcy t metod pokazany jest niej.
Dim conn As New SqlConnection(connetionString) Try conn.Open() Dim cmd As New SqlCommand(Select count(*) From Klienci, conn) Dim count As Integer = CInt(cmd.ExecuteScalar()) Cmd = Nothing conn.Close() conn = Nothing

1.4.3

Metoda ExecuteNonQuery

Metoda uruchamia procedur akcyjn typu insert (wstawienie rekordu), delete (usunicie) lub update (aktualizacj). Poniszy fragment kodu ilustruje uycie tej metody do usunicia rekordu o danej wartoci klucza.
Dim conn As New OleDbConnection(connetionString) Try

conn.Open() Dim cmd As New OleDbCommand(Delete From Klienci Where idk = 1, _ conn) cmd.ExecuteNonQuery() conn.Close() conn = Nothing

1.5 Obiekt DataReader


Jak wczeniej ju powidzieem obiekt DataReader pozwala na bardzo szybkie pobranie z bazy danych rekordestu, ktry moemy dalej wykorzysta np. jako rdo danych do takich obiektw jak ListBox, ComboBox czy DataGridView. Obiekt DataReader udostpnia trzy wane metody: Read, NextResult oraz GetShemaTable.

1.5.1

Metoda Read

Metoda Read pozwala na przeczytanie zawartoci tego obiektu po pobraniu danych (czyli rekordw) odczytajc wskazane pola przy pomiocy waciwoci Item o podanym indeksie. Musimy pamita o tym, e po utworzeniu tego obiektu wskanik rekordw ustawiany jest na zerwej pozycji i e moemy te rekordy odczytywa tylko w przd (od zerowego do ostatniego indeksu). Oczywicie nie ma moliwoci modyfikacji zestawu rekordw. Poniej kod procedury odczytujcej zawarto obiektu DataReader (procedura ta jest wmontowana w formularz frmOleDbDataReader jako reakcja na klik przycisku o nazwie btnMetodaRead).
Imports System.Data.OleDb Private Sub btnMetodaRead_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnMetodaRead.Click ' dekarujemy i tworzymy obiekt typu OLEDBConnetion Dim conn As New OleDbConnection(strconnOle) ' reszta operacji moe spowodowa bd, dlatego umieszcamy je w Try-Catch Try ' otwieramy poczenie conn.Open() ' deklarujemy i tworzymy obiekt typu OleDBCommand ' przekazujc do niego zapytanie SQL oraz otwarte poczenie Dim oleDBCmd As New OleDbCommand("select * from Pracownicy", conn) ' deklarujemy obiekt typu DBReader i wypeniamy go danymi ' poprzez wywoanie metody ExecuteReader obiektu OleDBCommand Dim oleDbReader = oleDBCmd.ExecuteReader ' czytamy obieky oleDbReader Dim i As Integer, txt As String, j As Integer = 1

While oleDbReader.Read txt = "Rekord nr " & j.ToString & ": " For i = 0 To oleDbReader.FieldCount - 1 txt &= oleDbReader.Item(i) & "; " Next MsgBox(txt.Substring(0, txt.Length - 2), _ MsgBoxStyle.Information, _ "Ilustaracja metody Read obiektu DataReader") j += 1 End While oleDBCmd = Nothing MsgBox("Wszystkie rekordy odczytane", MsgBoxStyle.Information, _ "Ilustaracja metody Read obiektu DataReader") Catch ex As Exception MsgBox("Problem z wykonaniem polecenia", _ MsgBoxStyle.Critical, _ "Ilustaracja metody Read obiektu DataReader") Finally conn.Close() conn = Nothing End Try End Sub

1.5.2

Metoda NextResult

Metoda NextResult moe by wykorzystywana w tych zapytaniach, w ktrych zaley nam na zwrceniu wicej ni jednego zestawu rekordw przy jednym poczeniu z baz danych. Poniej kod procedury zaimplementowanej w formularzu frmOleDbDataReader jako reakcji na klik przycisku o nazwie btnMetodaNextResult).
Imports System.Data.SqlClient Private Sub btnMetodaNextResult_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnMetodaNextResult.Click Dim sql1, sql2 As String sql1 = "select * from Klienci;" sql2 = "select * from RodzajSal" ' dekarujemy i tworzymy obiekt typu SqlConnetion Dim conn As New SqlConnection(strconnSqL) ' reszta operacji moe spowodowa bd, dlatego umieszcamy je ' w Try-Catch Try ' otwieramy poczenie conn.Open()

10

' deklarujemy i tworzymy obiekt typu sqlcommand ' przekazujc do niego zapytanie SQL oraz otwarte poczenie Dim sqlCmd As New SqlCommand(sql1 & sql2, conn) ' deklarujemy obiekt typu DBReader i wypeniamy go danymi ' poprzez wywoanie metody ExecuteReader obiektu OleDBCommand Dim sqlReader = sqlCmd.ExecuteReader ' czytamy obieky oleDbReader Dim i As Integer, txt As String, j As Integer = 1 While sqlReader.Read txt = "Rekord nr " & j.ToString & ": " For i = 0 To sqlReader.FieldCount - 1 txt &= sqlReader.Item(i) & "; " Next MsgBox(txt.Substring(0, txt.Length - 2), _ MsgBoxStyle.Information, "Tabela Klienci") j += 1 End While ' urucgamiamy pobranie rekordsetu z drugiego polecenia sqlReader.NextResult() ' bedziemy czyta rekordy, ustawiamy zmienn j na 1 j = 1 While sqlReader.Read txt = "Rekord nr " & j.ToString & ": " For i = 0 To sqlReader.FieldCount - 1 txt &= sqlReader.Item(i) & "; " Next MsgBox(txt.Substring(0, txt.Length - 2), _ MsgBoxStyle.Information, "Tabela RodzajSal") j += 1 End While sqlCmd = Nothing MsgBox("Wszystkie rekordy odczytane z obu tabel", _ MsgBoxStyle.Information, _ "Ilustaracja metod Read i NextResult obiektu DataReader") Catch ex As Exception MsgBox("Problem z wykonaniem polecenia", _ MsgBoxStyle.Critical, _ "Ilustaracja metod Read i NextResult obiektu DataReader") Finally conn.Close() conn = Nothing End Try End Sub

11

1.5.3

Metoda GetSchemaTable

W momencie utworzenia obiektu DataReader na podstawie zapytania sformuowanego w zapytaniu moemy pobra informacje o kolumnach tego zapytania poprzez wywoanie metody GetSchemaTable. Metoda ta zwraca obiekt typu DataTable, ktry zawiera tyle wierszy, ile jest zwracanych w zapytaniu. Z pomoc zagniedonych ptli For Each mona przejrze kolekcj wierszy i kolumn obiektu DataTable odczytujc nazwy waciwoci poszczeglnych kolumn tabeli bazy danych, do ktrych odnosi si zapytanie oraz ich wartoci. Poniej kod procedury zaimplementowanej w formularzu frmOleDbDataReader jako reakcji na klik przycisku o nazwie btnGetSchemaTable).
Imports System.Data.SqlClient Private Sub btnGetSchemaTable_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnGetSchemaTable.Click ' dekarujemy i tworzymy obiekt typu SqlConnetion Dim conn As New SqlConnection(strconnSqL) ' reszta operacji moe spowodowa bd, dlatego umieszcamy je w Try-Catch Try ' otwieramy poczenie conn.Open() ' deklarujemy i tworzymy obiekt typu sqlcommand ' przekazujc do niego zapytanie SQL oraz otwarte poczenie Dim sqlCmd As New SqlCommand("select * from Klienci", conn) ' deklarujemy obiekt typu DBReader i wypeniamy go danymi ' poprzez wywoanie metody ExecuteReader obiektu OleDBCommand Dim sqlReader = sqlCmd.ExecuteReader ' tworzymy obiekt DataTable i pobieramy schemat tabeli Dim schemaTable As DataTable = sqlReader.GetSchemaTable() pomocnicze zmienne dla odczytania schematu Dim row As DataRow Dim column As DataColumn ' przegldamy wiersz po wierszu obiekt DataTable ' kady wiersz opisuje jedn kolumn tabeli bazy danych For Each row In schemaTable.Rows Dim txt As String = "" For Each column In schemaTable.Columns txt &= column.ColumnName & ",: " & row(column).ToString & _ vbCrLf Next MsgBox(txt) Next sqlReader.Close()

12

Catch ex As Exception MsgBox("Problem z wykonaniem polecenia", MsgBoxStyle.Critical, _ "Ilustracja metody GetSchemaTable DataReader") Finally conn.Close() conn = Nothing End Try End Sub

1.6 Obiekt DataAdapter


Obiekt DataAdapter zabezpiecza komunikacj midzy obiektem DataSet i rdem danych (DataSource). DataAdapter pozwala, dziki metodzie Fill, na dostarczenie danych do obiektu DataSet (dokadniej do obiektu DataTable, ktry jest tworzony w DataSet). Inne metody obiektu DataAdapter pozwalaj take na komunikacj odwrotn, czyli na wprowadzanie, aktualizacj czy usuwanie danych w rdle (w DataSource).

1.6.1

DataAdapter, polecenie Select

Poniej fragment kodu realizujcego zadanie pobrania danych ze rda i umieszczeniu ich w obiekcie typu DataSet.
Imports System.Data.SqlClient ' inne potrzebne instrukcje Dim conn As New SqlConnection(strconn) Dim ds As New DataSet, i As Integer Try conn.Open() Dim adapter As New SqlDataAdapter("select * from Klienci", conn) adapter.Fill(ds) For i = 0 To ds.Tables(0).Rows.Count - 1 MsgBox(ds.Tables(0).Rows(i).Item(1)) Next Catch ex As Exception MsgBox(ex.ToString) Finally conn.Close() conn = Nothing End Try

13

1.6.2

DataAdapter, polecenia modyfikujce

Poniej fragment kodu procedury, ktrej zadaniem jest wprowadzenie rekordu do tabeli WykazSal, ktra zawiera trzy pola: ids jako pole typu integer z automatyczn inkrementacj o jeden (jest kluczem tej tabeli), NrSali, pole typu tekstowego o dugoci do 5 znakw oraz LiczbaMiejsc, pole typu integer
Imports System.Data.SqlClient Private Sub WprowadzRekord(ByVal strconn As String, _ ByVal txtNumer As String, ByVal Miejsc As Integer) Dim conn As New SqlConnection(strconn) Dim adapter As New SqlDataAdapter Dim sql As String sql = "Insert into WykazSal (NrSali, LiczbaMiejsc) values(" & _ NrSali & ", " & LiczbaMiejsc & ")" Try conn.Open() adapter.InsertCommand = New SqlCommand(sql, conn) adapter.InsertCommand.ExecuteNonQuery() MsgBox("Wiersz wstawiony") Catch ex As Exception MsgBox(ex.ToString) Finally conn.Close() conn = Nothing End Try End Sub

1.6.3

DataAdapter i CommandBuilder

Z obiektem DataAdapter wykonujcym zapytanie wybierajcje wszystkie pola z danej tabeli (czyli polecenie select) wsppracuje obiekt CommandBuilder, ktry potrafi wygenerowa polecenia delete, update i insert na podstawie zapytania wybierajcego. Wygenerowane zapytania aktualizujce mona wykorzysta do aktualizacji danych w rdle danych. Poniej fragment kodu ilustrujcy takie dziaanie. Imports System.Data.OleDb Dim conn As New OleDbConnection(constring) Dim oleDbCmdBuilder As OleDbCommandBuilder Dim ds As New DataSet, i As Integer Try conn.Open() Dim oledbAdapter As New OleDbDataAdapter( _ select * From Pracownicy, conn) oleDbCmdBuilder = New OleDbCommandBuilder(oleDbAdapter) oleDbAdapter.Fill(ds)

14

With ds.Tables(0) For i = 0 To .Rows.Count - 1 .Rows(i).Item(2) &= ".pl" Next End with oleDbAdapter.Update(ds.Tables(0)) MsgBox("Adresy mailowe uzupenione") Catch ex As Exception MsgBox(ex.ToString) Finally conn.Close() conn = Nothing End Try

1.7 Procedury przechowywane w VB.NET


Bardzo siln stron serwera SQL jest moliwo zaprojektowania w bazie danych utworzonej na tym serwerze zapytania napisanego w jzyku Transact-SQL, ktre nastpnie jest kompilowane i dopiero w takiej postaci wykonywane. Fakt komilacji zapytania znakomicie przyspiesza jego wykonanie i to jest pierwsza korzy z procedur przechowywanych. Inna, nie mniej wana zwizana jest z bezpieczestwem bazy danych, a zwizana jest z faktem, e mona tak skonfigurowa uprawnienia, aby moliwo manipulowania danymi bya jedynie za porednictwem procedur przechowywanych. Dziki temu nie ma adnej moliwoci wykonania jawnego kodu SQL. Procedura przechowywana moe by rnego typu, zarwno wybierajca jak i akcyjna. Z reguy bdziemy wykorzystywa procedury z argumentami, zarwno wchodzcymi jak i wychodzcymi. Mog si take zdarzy procedury bezargumentowe, raczej wybierajce ni akcyjne.

1.7.1

Procedura bezargumentowa

Powiedzmy, e w bazie danych, do ktrej bdziemy si odwoywa istnieje tabela Klienci. Powiedzmy dalej, e chcemy z niej odczyta identyfikator klienta oraz jego nazw. Procedura przechowywana moe mie nastpujc konstrukcj:
Create procedure dbo.pPobierzDane As Select idk, Nazwa From Klienci order by Nazwa

Po stronie aplikacji (VB.NET) pobranie tych danych i przypisanie ich jako rdo danych do obiektu typu ComboBox tak, aby pole Nazwa byo wywietlane w rozwijanej licie,

15

a pole idk byo zwracane po wybraniu nazwy klienta w licie moe by zrealizowane za pomoc niej pokazanych instrukcji.
Imports System.Data.SqlClient Dim conn As New SqlConnection(strconn) Dim cmd As New SqlCommand ' deklarujemy i tworzymy instancj obiektu dataset Dim ds As New DataSet Dim i As Integer ' reszta dziaa moe spowodowa bdm std uycie bloku ' Tray-Catch Try conn.Open() ' przypisanie do waciowoci Connection obiektu cmd ' otwartego poczenia cmd.Connection = conn ' okrelenie typu polecenia cmd.CommandType = CommandType.StoredProcedure ' podanie nazwy procedury przechowywanej cmd.CommandText = "dbo.pPobierzDane" ' przygotowania do odebrania danych, ktre zwrci procedura ' deklarujemy obiekt typu DataReader Dim dr As SqlDataReader ' poprzez wywoanie metody ExecuteReader dostarczamy dane ' do DataReader dr = cmd.ExecuteReader ' deklarujemy i tworzymy obiekt typu Dataset Dim dt As New DataTable ' wywoujemy metod Load utworzonego obiektu wskazujc jako ' rdo danych obiekt dr (DataReader) dt.Load(dr) ' tworzymy rdo danych dla cboKlienci (ComboBox) With Me.cboKlienci .DataSource = dt .DisplayMember = Nazwa .ValueMember = idk End With Catch ex As Exception MsgBox(ex.ToString) Finally conn.Close() conn = Nothing End Try

16

1.7.2

Procedura z parametrami

Tak jak wspomniaem wczeniej w wikszoci przypadkw procedura przechowywana bdzie posiadaa parametry (argumenty). Przy jej wywoywaniu z aplikacji musimy utworzy kolekcj parametrw. Dla kadego z nich musimy okreli takie elementy jak nazwa parametru, jego typ, kierunek, rozmiar czy warto. Powiedzmy, e mamy inn procedur w naszej bazie, ta rwnie zwraca klientw, ale bdziemy ich dodatkowo rozrnia z uwagi na warto pola StatusKlienta, jest to pole typu integer.
Create procedure dbo.pPobierzKlientaWgStatusu @statusK int As Select idk, Nazwa From Klienci where StatusKlienta = @statusK order by Nazwa

Poniej pokazany jest fragment kodu wywoujcy t procedur, zwrcony zestaw rekordw bdzie rdem danych dla formantu typu ListBox. Uytkownikowi pokaemy nazw klienta (pole Nazwa), po wyborze pozycji z listy chcemy otrzyma warto pola idk. Bdziemy pobierali list tych klientw, dla ktrych pole StatusKlienta ma warto np. 2.
Imports System.Data.SqlClient Dim conn As New SqlConnection(strconn) Dim cmd As New SqlCommand ' deklarujemy i tworzymy instancj obiektu dataset Dim ds As New DataSet Dim i As Integer ' reszta dziaa moe spowodowa bdm std uycie bloku ' Tray-Catch Try conn.Open() ' przypisanie do waciowoci Connection obiektu cmd ' otwartego poczenia cmd.Connection = conn ' okrelenie typu polecenia cmd.CommandType = CommandType.StoredProcedure ' podanie nazwy procedury przechowywanej cmd.CommandText = "dbo.pPobierzDane" ' zerujemy kolekcj parametrw cmd.Parameters.Clear() ' deklarujemy nowy parametr Dim param As SqlParameter param = New SqlParameter("@idk", SqlDbType.Int, 0)

17

param.Value = 2 ' okrelamy, czy parametr jest wchodzcy czy wychodzcy param.Direction = ParameterDirection.Input ' dodajemy utworzony parametr do kolekcji parametrw cmd.Parameters.Add(param) Dim dr As SqlDataReader dr = cmd.ExecuteReader Dim dt As New DataTable dt.Load(dr) ' tworzymy rdo danych dla lstKlienci (ListBox) With Me.lstKlienci .DataSource = dt .DisplayMember = Nazwa .ValueMember = idk End With Catch ex As Exception MsgBox(ex.ToString) Finally conn.Close() conn = Nothing End Try

W aplikacji ADOAndADONET zainteresowany Czytelnik znajdzie wiele przykadw wykorzystania modelu ADO.NET jak i starszego modelu ADO do przetwarzania informacji zgromadzonych w bazach danych.

18

Aplikacja ADOandADONET Aplikacja zostaa przygotowana w celu zademonstrowania podstawowych metod modelu ADO.NET oraz jego poprzednika ADO w zakresie dostpu do baz danych typu MS Access oraz MS SQL Server.

1.8 Wykorzystywane rda danych.


Aplikacja korzysta z bazy danych TestSP.mdb zawierajcej trzy przykadowe tabele o nazwach Studenci, Pracownicy i Przedmioty. Definicje tych tabel pokazane s niej.

Dla zademonstrowania wsppracy z baz SQL wykorzystywana bdzie tabela o nazwie Klienci i definicji jak niej. Tabela ta moe by umieszczona w dowolnej bazie danych na lokalnym czy zdalnym serwerze MS SQL Server. Istotne jest, abymy mieli prawo modyfikowania danych w tej tabeli.

Bdziemy take korzysta z trzech procedur przechowywanych, ich definicje pokazane s niej. Pierwsza z nich pobiera wszystkie dane z tej tabeli (instrukcj select), druga wykonuje instrukcj update (aktualizacji), a ostatnia odpowiada za wstawienie danych nowego klienta (instrukcja insert).
Create procedure [dbo].[pDajDaneKlientow] as select * from dbo.Klienci order by Nazwa

19

Create procedure [dbo].[pUpdateKlienci] @idk int, @nazwa nvarchar(100), @adres nvarchar(100), @nip nvarchar(13), @mail nvarchar(50), @telefon nvarchar(50), @osoba nvarchar(100) as update dbo.Klienci set Nazwa=@nazwa, Adres=@adres, NIP=@nip, mail=@mail, telefon=@telefon, OsobaKontaktowa=@osoba where idk=@idk Create procedure [dbo].[pWstawKlienta] @naz nvarchar(100),@adres nvarchar(100), @nip nvarchar(13), @mail nvarchar(50), @tel nvarchar(50), @osoba nvarchar(100), @idk int out as insert into dbo.Klienci (Nazwa, Adres, NIP, mail, telefon, OsobaKontaktowa) values (@naz, @adres, @nip, @mail, @tel, @osoba) set @idk=scope_identity()

1.9 Struktura aplikacji


Aplikacja zostaa zaprojektowana jako wieloformularzowa, zawiera formularz gwny o nazwie frmMDIForm, bdzie on peni rol kontenera dla pozostaych formularzy. Dodano do niego formant MenuStrip w celu zbudowania menu aplikacji. Poniej widok projektu tego formularza z projektem menu.

Jako gwne pozycje w pasku menu umieszczono: SQL bdzie zawiera polecenia zwizane z wykorzystaniem modelu ADO.NET do wsppracy z baz MS SQL Server;

20

OLEDB tu bd zgromadzone polecenia demonstrujce rne aspekty wykorzystania modelu ADO.NET do manipulowania danymi przykadowej bazy danych (TestSP.mdb);
ADO (COM) dwa polecenia podrzdne w tej grupie poka jak mona korzysta z

starego modelu ADO do uzyskiwania informacji o strukturze bazy danych (o tabelach i polach wybranej tabeli). Bdzie tu take pokazany sposb dynamicznego zbudowania formularzy ekranowych dla tabeli o nieznanej wczeniej strukturze (w pewnym sensie jako ciekawostka programistyczna). Do rozwizania dodano plik moduu wsplnego o nazwie ADOWspolny.vb, bdzie on zawiera deklaracje staych i zmiennych o charakterze publicznym dla projektu, czyli takich, ktre musz by dostpne dla pozostaych klas wykorzystywanych w tym rozwizaniu. W projekcie umieszczono autorsk klas CForStorageSub zawierajc szereg metod uatwiajcych dostp do procedur przechowywanych oraz klas CTestSQL jako jej klas pochodn. Instancje klasy CTestSQL bd odpowiedzialne za wykonanie polece zwizanych z pozycj SQL w menu aplikacji. Klasa CMojeOle definiuje metody i waciwoci zwizane z wykonywaniem polece zwizanych z pozycj OLEDB w menu aplikacji.

1.10 Dodatkowe referencje.


Z uwagi na zamiar wykorzystywania modelu ADO musimy doda do naszego rozwizania referencje do odpowiednich bibliotek (jak pokazano niej).

21

Po ich dodaniu lista referencji naszego rozwizania powinna wyglda tak, jak jest to pokazane niej (wane, aby byy tam dwie pokazane wczeniej pozycje: adodb oraz Microsoft ADO Ext. 6.0 for DDL and security).

22

1.11 Modu wsplny


Jak wspomniaem wczeniej mamy tu deklaracje staych i zmiennych publicznych, czyli takich, ktre musz by dostpne we wszystkich innych obiektach tej aplikacji.

1.12 Klasa CMojeOle


Klasa ta przeznaczona jest do obsugi baz danych typu MS Access, dlatego pierwsze jej dwie instrukcje importuj odpowiednie przestrzenie nazw. W kodzie klasy zadeklarowano trzy zmienne prywatne, bd one wykorzystywane przez metody tej klasy. Widoczny jest take konstruktor bezparametrowy tej klasy.

23

Kolejny konstruktor (procedura publiczna o nazwie New) odpowiada za poczenie ze rdem danych wskazanym informacj zawart w argumencie strconn oraz wykonanie polecenia SQL przekazanego argumentem strSql.

Kolejny konstruktor New bdzie odpowiada za powizanie formularza zawierajcego etykiety i pola tekstowe odpowiadajce konkretnej tabeli bazy danych z danymi pobranymi z tej tabeli. Zakadamy, e nazwy pl tekstowych odpowiadajcych polom tej tabeli zaczynaj si od prefiksu txt, jest to bardzo wane, jeeli mamy je powiza z danymi (inaczej bdzie bd czasu wykonania). Z tego warunku wynika take ograniczenie na nazwy pl bazy danych nazwy te nie mog zawiera spacji!.

24

Kolejny konstruktor bdzie tworzy instancj klasy CMojeOle wykorzystywan do pobrania ze rda danych informacji, ktre maj by dostarczone do formantu typu ListBox jako jego rdo danych z jednoczesnym wskazaniem, jakie informacje maj by widoczne dla uytkownika oraz jaka informacja ma by zwrcona w momencie kliku danej pozycji listy. Z reguy w formularzu zawierajcym formanty typu ListBox, CheckedListBox czy ComboBox tworzona jest procedura umoliwiajce pobranie informacji o wskazanej przez uytkownika pozycji. Procedura taka obsuguje zdarzenie SelectedIndexChange, a jest wywoywana nie tylko w momencie kliku wybranej pozycji, lecz take w momencie definiowania rda danych takiego formantu. W celu zablokowania takiej niepodanej reakcji wykorzystamy zmienn publiczn flaga ustawiajc jej warto na False na czas definiowania rda danych.

25

W klasie CMojeOle utworzyem jeszcze jeden konstruktor, instancja klasy utworzona z jego pomoc buduje obiekt typu DataTable bdcy poczeniem dwch tabel. Konstruktor zosta wymylony po to, aby mona byo uzyska rdo danych dla formantu typu ComboBox, gdzie na pierwszej pozycji bdzie pozycja opisujca symbolicznie wszystkie pozostae pozycje. Z reguy polega to na umieszczeniu na pocztku listy informacji typu (wszystko) czy (uwzgldniajc wymogi jzyka polskiego) (wszyscy) lub (wszystkie). Wybranie takiej pozycji jest wtedy odpowiednikiem zapytania select lista_kolumn from nazwa_tabeli. Z kolei wybranie innej pozycji jest odpowiednikiem zapytania jak wyej, ale uzupenionego warunkiem where. Przedstawiony niej konstruktor realizuje przygotowanie odpowiedniego rda danych. Przy okazji jego kod pokazuje, jak mona manipulowa takim obiektem jak DataTable poprzez

26

dodawanie nowych kolumn i wierszy, a take doczenie do jednej tabeli innej tabeli o takiej samej strukturze.

27

Uzupenieniem kodu klasy CMojeOle s jeszcze trzy procedury typu Property zwracajce wartoci wybranych zmiennych prywatnych.

Poniej widok diagramu UML klasy CMojeOle, jest na nim widoczna metoda o nazwie MergeDataTable, zostaa ona zastpiona przez ostatni z przedstawionych konstruktorw jako bardziej uniwersalne rozwizanie.

W grupie Methods widzimy informacj o konstruktorach, o tym, e istnieje w sumie pi konstruktorw (czyli mamy do czynienia z przecieniem metod).

28

1.13 Opis pozycji OLEDB w menu


1.13.1 Adapter
Wskazana pozycja menu otrzymaa nazw mnuApapterPracownicy, a jej klik spowoduje wykonanie pokazanej niej procedury.

Procedura deklaruje i tworzy nowy egzemplarz formularza frmOleAdapterCmdB. W kolejnej instrukcji do zmiennej publicznej strcostam przypisywane jest zapytanie wybierajce z tabeli Pracownicy wszystkie rekordy zawierajce komplet pl. Tre tego zapytania jest take przypisywana do tytuu utworzonego formularza. Ostatnie dwie instrukcje okrelaj formularz nadrzdny dla utworzonego formularza oraz nakazuj jego wywietlenie. Formularz frmOleAdapterCmD jest stosunkowo prosty w sensie uytych formantw, zawiera bowiem tylko dwa formanty o nazwach: dgvDane formant typu DataGridView, bdziemy w nim wywietla pobrane dane; btnAktualizuj przycisk polecenia, jego klik wywoa procedur zdarzeniow odpowiedzialn za aktualizacj danych.

29

Znacznie ciekawszy jest kod tego formularza.

30

Klik przycisku btnAktualizuj uruchamia ponisz procedur.

A tak wyglda ten formularz w trakcie pracy, do ostatniego wiersza w polu Adres dopisano dwa kocowe znaki. Klik przycisku Aktualizuj zmodyfikowa dane w bazie danych.

31

1.13.2 DataReader
Polecenia zebrane w tej pozycji menu demonstruj sposb pobrania danych z bazy w celu zbudowania rda danych dla takiego formantu jak ListBox (analogicznie bdzie dla ComboBox). Klik podpozycji Studenci (nazwa mnuDRStudenci) uruchamia ponisz procedur (w frmMDIForm.vb).

Pierwsza instrukcja tej procedury deklaruje i tworzy nowy egzemplarz formularza o nazwie frmOleDataReader. Formularz ten zawiera jeden formant typu ListBox o nazwie cboPracownik. Do zmiennej globalnej strcostam przypisywane jest zapytanie zwracajce dwie kolumny danych, pierwsza z nich zwraca identyfikator studenta, drugie kombinacj dwch pl (nazwisko i imi). Istotne s tutaj nazwy tych kolumn, w przypadku pierwszej bdzie to nazwa pola, dla drugiej kolumny tworzymy nazw (tu jest to Kto). Kolejna instrukcja deklaruje i tworzy nowy egzemplarz klasy CMojeOle wykorzystujc odpowiedni kontruktor. W efekcie tworzone i otwierane jest poczenie z baz danych, za pomoc obiektu DataReader odczytywane s potrzebne dane i przypisywane do formantu listy. Jeeli wszystko przebiego poprawnie, to wasno Komunikat jest pustym cigiem znakw. Wykorzystujc warunek If Then Else End if badamy, czy operacja pobrania danych i zbudowania rda danych dla cboPracownik przebiega poprawnie. Jeeli tak, to pokazujemy formularz na ekranie, jak nie, to wywietlamy stosowny komunikat. W kodzie formularza frmOleDataReader mamy tylko jedn procedur, jej zadaniem jest zwrcenie informacji o identyfikatorze wybranej przez uytkownika pozycji listy. Warto zwrci uwag na pierwsz instrukcj tej procedury, jest tu badanie, czy zdarzenie ma by obsugiwane czy te nie. Jeeli zmienna globalna flaga nie jest True, to opuszczamy procedur.

32

Tradycyjnie na zakoczenie tego podrozdziau zrzut ekranowy pokazujcy prac tego formularza.

33

1.13.3 Przegld tabel


Ta pozycja menu OLEDB daje dostp do trzech polece odpowiedzialnych za wywietlenie indywidualnie zaprojektowanych formularzy dla tabel Pracownicy, Studenci i Przedmioty. Kady z tych formularzy wyposaony jest w zestaw przyciskw pozwalajcych na poruszanie si po rekordach tych tabel.

Podpozycja Pracownicy w menu Przegld tabel jest identyfikowana poprzez nazw mnuPPracownicy, a jej klik uruchamia pokazan niej procedur.

Pierwsza instrukcja tej procedury deklaruje i tworzy instancj (egzemplarz) formularza frmPracownicy (jego projekt bdzie pokazany za chwil). Kolejna instrukcja przypisuje do zmiennej prywatnej sql tre zapytania zwracajcego wszystkie rekordy z tabeli Pracownicy. Deklarowany i tworzony jest egzemplarz klasy CMojeOle wykorzystujcy jeden z konstruktorw tej klasy. Przekazujemy do niego acuch poczenia, tre zapytania oraz utworzony przed chwil formularz. W klasie nastpi otwarcie poczenia, pobranie potrzebnych danych, zwrcenie ich do obiektu typu DataSet oraz ustanowienie poczenia midzy polami

34

tekstowymi formularza a odpowiadajcymi im kolumnami w tabeli o nazwie Test w DataSet. Utworzony obiekt DataSet bdzie dostpny poprzez waciwo DajDS tej klasy. Jeeli wszystko przebiego poprawnie, to zmienna globalna myDataSet otrzymuje potrzebny obiekt z instancji klasy, tworzony jest tytu formularza i na kocu pokazujemy formularz. Gdyby co poszo nie tak, to zamiast formularza wywietlany jest stosowny komunikat. Poniej pokazany jest projekt formularza frmPracownicy.

Na jego powierzchni umieszczono pola tekstowe (TextBox) odpowiadajce polom tabeli Pracownicy. Kolejno s to: txtID tu bdzie wywietlany identyfikator pracownika txtNazwisko jego nazwisko; txtImie i imi; txtAdres oraz adres. Pola te poprzedzone s etykietami (Label), ktrych waciwoci Text zostay tak skomponowane, aby odpowiaday poszczeglnym polom. Poniej tych formantw umieszczono cztery przyciski o nazwach odpowiednio btnPierwszy, btnNastepny, btnPoprzedni oraz btnOstatni. Waciwoci Text tych przyciskw zostay dostosowane do ich przeznaczenia, ktrym jest wymuszenie przejcia do wskazanego rekordu. Midzy tymi przyciskami umieszczono jeszcze jedno pole tekstowe, jego nazwa to NrRekordu (nie moe zaczyna si od txt, bo wtedy bya by prba skojarzenia tego pola z kolumn w obiekcie DataSet, a takiej kolumny nie ma). W polu tym bdziemy wywietla numer biecego rekordu.

35

W kodzie klasy formularza frmPracownicy znajdziemy wiele ciekawych procedur. Prywatna procedura KtoryRekord ustala, jaki jest numer biecego rekordu, a nastpnie wywietla go w polu tekstowym NrRekordu. Jej kolejnym zadaniem jest ustalenie, ktre z przyciskw nawigacyjnych maj by dostpne, a ktre nie. Procedura obsugujca zaadowanie formularza wywouje tylko t prywatn procedur KtoryRekord.

Kolejno budujemy procedury obsugujce poruszanie si po rekordach.

36

Tradycyjnie dwa zrzuty ekranowe tego formularza w pracy.

37

1.14 Opis pozycji ADO (COM) w menu


Znajdziemy tu dwa polecenia, tak jak pokazano to niej.

Pierwsze z nich jest dostpne i pozwala na wskazanie pliku bazy danych MS Access w wersji 2003 (plik z rozszerzeniem *.mdb), jego otwarcie, pobranie listy tabel, a dla wybranej tabeli listy jej pl. Bdziemy take mogli zbudowa zapytanie SQL i zobaczy efekt jego dziaania. Drugie z polece jest chwilo niedostpne, a bdzie udostpnione dopiero po wskazaniu pliku bazy danych w pierwszym z pokazanych polece. Polecenie to jest ciekawe programistycznie, pokazuje bowiem jak programowo (dynamicznie) mona budowa formularze.

1.14.1 Pobieranie tabel i pl, test zapytania


Klik tego polecenia powoduje wykonanie pokazanej niej procedury.

Jak widzimy nic ciekawego tu nie, pierwsza instrukcja tworzy instancj klasy formularza frmTestSQL, a dwie kolejne odpowiadaj za wywietlenie formularza. Formularz frmTestSQL jest do skomplikowany, zawiera dwie listy, pole tekstowe do wywietlenia treci zapytania, formant typu DataGridView do wywietlenia wynikw zapytania oraz kilka przyciskw polece i etykiet opisujcych niektre formanty. Projekt formularza wraz z nazwami i przeznaczeniem poszczeglnych formantw pokazany jest niej.

38

lstTabele formant typu ListBox, bdzie wywietla tabele wybranej bazy danych; lstPola take lista, bd tu pokazywane nazwy pl wybranej tabeli; txtSQL pole tekstowe (TextBox), wywietlimy w nim zapytanie. Jego waciwo Multiline zostaa ustawiona na True, a wysoko formantu na 45 pkt (tak, aby mona byo pokaza dwa wiersze); dgvdane formant typu DataGridView, tu bdziemy prezentowa wyniki zapytania; btnAddWhere przycisk polecenia (Button), uruchamia dodanie do zapytania nazwy tabeli i sowa kluczowego Where; btnWykonaj przycisk uruchamiajcy procedur odpowiedzialn za wykonanie zapytania; btnClearSql przycisk polecenia usuwajcy wszelkie informacje z pola txtSQL. W kodzie klasy tego formularza umieszczono do du liczb procedur i funkcji odpowiedzialnych za jego funkcjonowanie. Kolejno bd je prezentowa.

39

Zaczynamy od zaimportowania niezbdnych przestrzeni nazw (do wsppracy z baz danych MS Access. Pierwsz wan funkcj w tej klasie jest GetFileName, funkcja odpowiedzialna za wywietlenie okna dialogowe typu OpenFileDialog i pobranie od uytkownika penej nazwy pliku bazy danych (nazwy i cieki do niego wiodcej).

Procedura obsugujce zdarzenia Load formularza korzysta z funkcji GetFileName. Jeeli uytkownik wskaza plik bazy danych, to wykorzystujc obiekt ADOX modelu ADO pobierane s informacje o tabelach tej bazy i umieszczane w licie lstTabele.

40

Ustalana jest liczna tabel dla wybranej bazy, cieka dostpu do niej umieszczane jest w zmiennej globalnej strConnDynamic, a polecenie Zbudowanie dynamicznego formularza zostaje udostpnione.

41

Pokazana niej procedura wykonywana jest w momencie, gdy uytkownik wybra jedn z tabel. Musimy wtedy przejrze kolekcj tabel tej bazy, znale t tabel, ktra zostaa wybrana, a nastpnie pobra list pl dla tej wybranej tabeli.

Nazwa pola (kolumny) moe (cho lepiej byoby gdyby nie) zawiera spacj. W zapytaniu taka nazwa musi by opakowana w nawiasy kwadratowe. Pokazana nie funkcja realizuje wanie to zadanie.

42

Kolejna procedura reaguje na klik jednej z pozycji w licie lstPola. Jej zadaniem jest skomponowanie treci zapytania wybierajcego umieszczonego w polu txtSql.

Klik przycisku Dodaj Where uruchamia procedur, ktrej zadaniem jest sprawdzenie, czy dotychczas skonstruowane zapytanie zawiera fraz From , jeeli nie, to taka fraza jest dodawana do zapytania, nastpnie dodawana jest nazwa tabeli pobrana z lstTabele (czyli wybr uytkownika). Do sprawdzenia, czy w zapytaniu wystpowaa fraza From wykorzystana zostaa waciwo LastIndexOf zmiennej typu String. Inn przydatn ciekawostk jest ustawienia fokusa (kursora) w polu tekstowym w taki sposb, aby jego zawarto nie zostaa zaznaczona.

43

Zadaniem prywatnej funkcji SprawdzZapytanie jest zbadanie, czy w treci zapytania mamy podan informacj z jakiej tabeli bdziemy pobiera dane. Jeeli wszystko jest OK., to funkcja zwraca True, jeeli nie, to wywietla stosowny komunikat i zwraca False.

44

Po skomponowaniu zapytania moemy sprawdzi jego funkcjonowanie poprzez klik przycisku opisanego jako Wykonaj (btnWykonaj), co powoduje wykonanie poniszej procedury. Pierwsza instrukcja tej procedury wywouje funkcj SprawdzZapytanie, jeeli funkcja zwraca False, to nastpuje wyjcie z procedury.

Do waciwoci Text etykiety lblWynikZapytania chcemy zwrci informacj o liczbie zwrconych rekordw. Z uwago na jzyk polski korzystamy z pomocniczej funkcji o nazwie OdmienRekordow, ktrej zadaniem jest zwrcenie poprawnej odmiany sowa rekordw.

Podobnie jak wczeniej pozostao ju tylko pokazanie dziaania tego formularza. Poniej dwa przykadowe zrzuty ekranowe. Zaczynamy od wskazania pliku bazy danych, tu jest to plik TestSP.mdb z dysku E:\ .

45

Klik przycisku Otwrz wywietla instancj formularza frmTestSql. Prosz zauway, e lista Dostpne tabele zawiera ju tabele (uytkownika) utworzone w wybranej bazie danych.

46

Klik jednej z tych tabel powoduje pobranie jej listy pl i umieszczenie ich nazw w licie Pola wybranej tabeli.

Jeeli w licie pl klikniemy np. symbol gwiazdki, to do pola Budowane zapytanie zostanie wstawiony poniszy tekst.

Klik przycisku Wykonaj (przy niekompletnym zapytaniu brakuje From Studenci) moe spowodowa dwojak reakcj. Albo zostanie zgoszony bd wynikajcy z konstrukcji zapytania albo funkcja SprawdzZapytanie poprawnie uzupeni zapytanie.

47

Klik przycisku powoduje taki efekt jak poniej. Wyranie widzimy, e funkcja SprawdzZapytanie niezbyt poprawnie skorygowaa nasze zapytanie, w rezultacie wystpi bd czasu wykonania, skwitowany pokazanym komunikatem.

Moemy rcznie poprawi zapytanie lub zastanowi si, jaki bd zosta popeniony w funkcji SprawdzZapytanie. Drugi sposb jest trudniejszy, ale my zajmiemy si poprawieniem funkcjonowania tej funkcji. Co si stao i dlaczego wywoanie tej funkcji zmienio wyjciowy tekst zapytania Select * na Select From Studenci zamiast Select * From Studenci ? Przyczyna jest stosunkowo prosta. W procedurze obsugujcej klik pozycji w licie lstPola jest fragment wskazany strzak.

48

Jeeli uytkownik wybra symbol gwiazdki, to do pola txtSql zosta wstawiony tekst Select * (po gwiazdce jest jeszcze spacja). Z kolei w oryginalnej funkcji SprawdzZapytanie jest taki fragment:

Jeeli w treci zapytania nie byo frazy From (a w naszym przykadzie nie byo), to tre zapytania zostaa skrcona o dwa znaki w naszym przypadku zostaa skasowana * ! Dotychczasowy fragment by dobrze pomylany w tych sytuacjach, gdy uytkownik wybra nie gwiazdk, ale konkretne pole. Wtedy do pola txtSql wstawiany jest tekst Select nazwa_pola, (po nazwie pola dodawany jest przecinek i spacja). W takich sytuacjach poprawienie zapytania polegajce na usuniciu dwch ostatnich znakw i dodanie frazy From jest poprawne! Rozwizanie jest banalnie proste, wystarczy w przypadku wybrania gwiazdki doda do instrukcji select nie gwiazdk i spacj, lecz dwie spacje!. Po tej korekcie wszystko jest OK.

49

1.14.2 Zbudowanie dynamicznego formularza


Korzystajc z obiektu ADOX moemy pobra informacje o strukturze bazy MS Access (o jej tabelach i polach tych tabel), co daje nam moliwo zbudowania formularza z formantem typu TabControl, gdzie w poszczeglnych zakadkach umiecimy zestaw etykiet i pl tekstowych wywietlajcych pojedyncze rekordy dla kadej z tabel Projekt formularza frmPojedynczyRekord jest stosunkowo prosty, umieszczono w nim wspomniany wyej formant TabControl nadajc mu nazw tbcZakladki.

Wysoko formantu tbcZakladki zostaa tak dobrana, aby na dole formularza pozosta wolny pasek, w ktrym umieszczono cztery przyciski do obsugi nawigacji po rekordach oraz pole tekstowe do wywietlenia numeru biecego rekordu. W prezentowanym rozwizaniu wysoko formularza zostaa ustawiona na 300 pkt, szeroko na 496 pkt. Z kolei wysoko formantu tbcZakladki ustawiono na 227 pkt przy szerokoci 475 pkt. Formant ten zosta ustawiony na pozycji 2, 2 wzgldem lewego grnego naronika formularza. Przyciski btnPierwszy, btnNastepny, btnPoprzedni oraz btnOstatni bd funkcjonowa jako wsplne przyciski nawigacyjne pozwalajc na przemieszczanie kursora po zestawie rekordw danej tabeli. Midzy nimi umieszczono pole txtNrRekordu, bdzie w nim wywietlany numer biecego rekordu dla biecej zakadki (dokadniej: dla tabeli prezentowanej na biecej zakadki). Poniej peny kod klasy formularza frmPojedynczyRekord, prezentowane s wszystkie procedury i funkcje zabezpieczajce funkcjonowanie tego formularza.

50

Imports System Imports System.Data ' import przestrzeni nazw niezbdnej do wsppracy z baz MS Access Imports System.Data.OleDb Public Class frmPojedynczyRekord Private nrZakladki As Integer Private myDataSet As New DataSet Private myAdapter As OleDbDataAdapter ' tablica X() bdzie przechowywa numery biecych ' rekordw dla kadego z formularzy (tabeli) Private X() As Long Private tabela As String Private Sub frmPojedynczyRekord_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Load Dim adoConn As New ADODB.Connection, i As Integer = 0 ' deklaracja i utworzenie obiektu ADOX.Catalog Dim myCat As New ADOX.Catalog ' deklaracja zmiennej tbl typu tabela obiektu ADOX na ' potrzeby ptli For Each Dim tbl As ADOX.Table, col As ADOX.Column ' otwieramy poczenie z baz danych, wywoujemy metod ' Open obiektu adoConn adoConn.Open(strConnDynamic) ' wskazujemy aktywne poczenie dla obiektu myCat myCat.ActiveConnection = adoConn ' deklaracja formantw Dim myLBl As Label Dim myTXT As TextBox Dim j, nrZ As Integer, yl As Integer = 10 Dim yt As Integer = 10 ' deklaracja zakadki formantu TabControl Dim myTab As TabPage nrZ = -1 For Each tbl In myCat.Tables ' jeeli nie jest to tabela systemowa MS Access If tbl.Name.Substring(0, 2) <> "MS" Then nrZ += 1 ' tworzymy egzemplarz nowej zakadki myTab = New TabPage ' komponujemy jej nazw myTab.Name = "tab" & tbl.Name & "_" & nrZ.ToString ' definuyjemy jej tytu myTab.Text = tbl.Name

51

' dodajemy utworzon zakadk do formantu TabControl Me.tbcZakladki.Controls.Add(myTab) ' a teraz dodajemy pola tekstowe i etykiety pl dla danej tabeli j = 0 For Each col In tbl.Columns ' tworzymy egzemplarz etykiety myLBl = New Label ' waciwoci Text przypisujemy nazw kolumny myLBl.Text = col.Name ' tworzymy nazw etykiety myLBl.Name = "lbl" & UsunSpacje(col.Name) ' definiujemy wyrwnanie tekstu w etykiecie myLBl.TextAlign = ContentAlignment.MiddleRight ' ustawiamy waciowo Location ' pierwszy argument jest stay i okrela odsunicie ' etykiety w poziomie od lewej krawdzi formantu ' drugi argument okrela pooenie pionowe i jest ' dynamicznie zmieniany myLBl.Location = New System.Drawing.Point(15, j * 30 + 13) ' ustawiamy take rozmiar etykiety (stay, bo nie wiadomo ' jaki bdzie potrzebny) myLBl.Size = New System.Drawing.Size(80, 13) ' tworzymy nowy egzemplarz formantu TextBox myTXT = New TextBox ' dynamicznie ustalamy jego pooenie myTXT.Location = _ New System.Drawing.Point(100, j * 30 + 10) ' okrelamy rozmiar (szeroko, wysoko) tego formantu myTXT.Size = New System.Drawing.Size(330, 20) ' nadajemy mu nazw skomponowan z prefiksu 'txt' i nazwy ' kolumny myTXT.Name = "txt" & col.Name ' dodajemy oba formanty do kolekcji formantw utworzonej ' zakadki myTab.Controls.Add(myLBl) myTab.Controls.Add(myTXT) ' powikszamy zmienn j o jeden (liczba par pl ' tekstowych i etykiet j += 1 Next ' wywoujemy prywatn procedur, jej zadaniem jest pobranie ' danych z bazy i przypicie ich do odpowiednich pl ' tekstowych

52

PobierzDane(nrZ) End If ' pobranie danych z tabeli zakoczone, uruchamiamy kolejn ' Ptl (obrt) Next ' sprztamy po sobie adoConn.Close() adoConn = Nothing ' jeeli w bazie bya co najmniej jedna kolumna, to If nrZ > -1 Then ' zmieniamy rozmiar tablicy X() ReDim X(nrZ) ' w ptli ustawiamy zero na kadej pozycji tej tablicy For i = 0 To nrZ X(i) = 0 ' ustawienie zerowego rekordu dla kaej zakadki Next ' zakadk biec bdzie ta o indeksie 0 nrZakladki = 0 Me.tbcZakladki.TabPages.Item(nrZakladki).Select() ' wywoujemy procedur wywietlajc numer aktualnego rekordu ' dla biecej zakadki KtoryRekord() Else MsgBox("Nie odczytano adnej tabeli uytkownika") End If End Sub Private Function UsunSpacje(ByVal nazwa As String) As String ' jeeli nazwa pola zawiera spacje, to bd zastpione podkreleniem Return nazwa.Replace(" ", "_") End Function Private Sub PobierzDane(ByVal nrZ As Integer) ' ustawiamy zakadk o indeksie nrZ jako aktywn Me.tbcZakladki.TabPages(nrZ).Select() Dim txtSql As String, i As Integer tabela = tbcZakladki.TabPages(nrZ).Text ' zbudowanie dynamicznego zapytania select * from nazwa_tabeli txtSql = "select * from " & tabela ' deklarujemy i tworzymy instancj obiektu Connection Dim jg As New OleDbConnection(strConnDynamic) ' kolejne instrukcje w bloku Try - Catch Try jg.Open() ' otwarcie poczenia

53

' zbudowanie egzemplarza obiektu Adapter w oparciu ' o zdefiniowane zapytanie i otwarte poczenie myAdapter = New OleDbDataAdapter(txtSql, jg) ' wywoanie metody Fill z poleceniem przeniesienia do ' obiektu myDataSet pobranych rekordw, zaczynajc od rekordu ' o numerze 0, z okreleniem maksymalnej liczby rekordw, ' pobrane dane maj utworzy obiekt DataSet o nazwie tabela ' do zmiennej 'i' metoda Fill zwraca liczb pobranych rekordw i = myAdapter.Fill(myDataSet, 0, MaxLiczbaRekordow, tabela) ' jeeli pobrano jakie rekordy, ... If i > 0 Then ' deklaracja obiektu ctl na potrzeby ptli For Each Dim ctl As Control ' przegldamy kolekcj kontrolek wskazanej zakadki For Each ctl In tbcZakladki.TabPages(nrZ).Controls ' jeeli aktualny formant jest polem tekstowym, to ... If TypeOf (ctl) Is TextBox Then ' wywoujemy metod DataBindings tego formantu ' nakazujc przypicie do waciwoci Text danych, ' Ktre bd pobierane z obiektu myDataSet z obiektu ' typu DataTable o nazwie 'tabela' z kolumny, ktrej ' nazwa jest zgodna z nazw formantu ctl po odrzuceniu ' prefiksu txt ctl.DataBindings.Add("Text", myDataSet, _ tabela & "." & ctl.Name.Substring(3, _ ctl.Name.Length - 3)) End If Next End If Catch ex As Exception MsgBox("Problem z pobraniem lub przypiciem danych do pl " & _ "tekstowych", MsgBoxStyle.Critical, "Dynamiczne formularze") Finally jg.Close()' sprztamy po sobie jg = Nothing End Try End Sub Private Sub btnPoprzedni_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnPoprzedni.Click ' Dim tabela As String = tbcZakladki.SelectedTab.Text Me.BindingContext(myDataSet, tabela).Position -= 1 KtoryRekord() End Sub

54

Private Sub tbcZakladki_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles tbcZakladki.Click tabela = tbcZakladki.SelectedTab.Text nrZakladki = Me.tbcZakladki.SelectedIndex Me.BindingContext(myDataSet, tabela).Position = X(nrZakladki) KtoryRekord() End Sub Private Sub btnNastepny_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnNastepny.Click Dim tabela As String = tbcZakladki.SelectedTab.Text Me.BindingContext(myDataSet, tabela).Position += 1 KtoryRekord() End Sub Private Sub btnPierwszy_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnPierwszy.Click Dim tabela As String = tbcZakladki.SelectedTab.Text Me.BindingContext(myDataSet, tabela).Position = 0 KtoryRekord() End Sub Private Sub btnOstatni_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnOstatni.Click Dim tabela As String = tbcZakladki.SelectedTab.Text Me.BindingContext(myDataSet, tabela).Position = _ myDataSet.Tables(tabela).Rows.Count KtoryRekord() End Sub Private Sub KtoryRekord() Dim pozycja As Long = _ Me.BindingContext(myDataSet, tabela).Position X(nrZakladki) = pozycja Me.txtNrRekordu.Text = "Rekord nr " & (pozycja + 1).ToString If pozycja + 1 = myDataSet.Tables(tabela).Rows.Count Then Me.btnOstatni.Enabled = False Me.btnNastepny.Enabled = False Else Me.btnOstatni.Enabled = True Me.btnNastepny.Enabled = True End If If pozycja = 0 Then Me.btnPierwszy.Enabled = False Me.btnPoprzedni.Enabled = False

55

Else Me.btnPierwszy.Enabled = True Me.btnPoprzedni.Enabled = True End If End Sub End Class

Instancja formularza frmPojedynczyRekord jest uruchamiana tylko wtedy, gdy wczenie wykonane zostao polecenie Pobranie tabel i pl, test zapytania z menu ADO (COM), poniewa wtedy do zmiennej globalnej strConnDynamic zostanie przypisana cieka do pliku bazy danych MS Access. Poniej stosowny fragment kodu z klasy formularza gwnego naszej aplikacji (czyli frmMDIForm).
Private Sub mnuDynamiczneFormularze_Click(ByVal sender As _ System.Object, ByVal e As System.EventArgs) _ Handles mnuDynamiczneFormularze.Click If strConnDynamic.Length > 0 Then Dim frm As New frmPojedynczyRekord frm.MdiParent = Me frm.Show() Else MsgBox("Polecenie niedostpne, baza danych nie jest znana") End If End Sub

Poniej widok zbudowanego formularza w tracie pracy, pokazany jest formularz ekranowy tabeli Pracownicy.

56

1.15 Opis pozycji SQL


Ten fragment aplikacji wykorzystuje przykadow tabel o nazwie Klienci, ktrej definicj przedstawiono w rozdziale 2.1 tej pozycji. Kolejno zostanie przedstawione wykorzystanie obiektu Adapter do przegldania i modyfikowania rekordw tej tabeli oraz bardziej zaawansowane przetwarzanie informacji z tej tabeli z pomoc procedur przechowywanych.

1.15.1 Adapter
Formularz frmAdapterSQLCmdB zawiera dwa formanty: DataGridView o nazwie dgvDane, w ktrym bdziemy prezentowa rekordy tabeli Klienci oraz przycisk polecenia (Button) o nazwie btnUpdate.

Przyciski minimalizacji i maksymalizacji formularza zostay pozostawione po to, aby uytkownik mg modyfikowa rozmiar tego formularza, jeeli bdzie zachodzia taka potrzeba. Z tych samych powodw pozostawiono moliwo rcznego zmodyfikowania rozmiarw tego formularza (waciwo FormBorderStyle ustawiona na Sizable). Z tych powodw (dopuszczenia modyfikacji rozmiarw formularza) zmodyfikowano waciwo Anchor obu formantw. W przypadku dgvDane zostaa ona ustawiona na zakotwiczenie tego formantu wg wszystkich czterech krawdzi formularza.

57

W przypadku przycisku btnUpdate waciwo Anchor ustawiono wg prawej i dolnej krawdzi formularza.

W kodzie klasy formularza zadeklarowano potrzebne zmienne oraz utworzono dwie procedury odpowiedzialne za jego funkcjonowanie. Poniej kod tych procedur wraz z obszernymi komentarzami.
' Import potrzebnych przestrzeni nazw Imports System.Data Imports System.Data.SqlClient Public Class frmSQLUpdateKlienci ' deklaracja zmiennych moduu dla klasy formularza ' ich deklaracja w tym miejscu powoduje, e bd dostpne ' dla wszystkich procedur i funkcji w tej klasie Dim sqlAdapter As SqlDataAdapter Dim ds As DataSet ' tablica xUpdate ma peni rol wskanika czy jest potrzebna ' aktualizacja Dim xUpdate() As Boolean Private Sub frmSQLAdapterCmdB_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Load ' dekalracja i utworzenie obiektu SQLConnection ' zmienna globalna strConnSql zawiera acuch poczenia do ' bazy SQL Dim conn As New SqlConnection(strconnSqL) ' utworzenie nowego egzemplarza obiektu DataSet ds = New DataSet ' dalsze instrukcje mog spowodowa bd, std s w bloku ' Try-Catch Try ' otwieramy poaczenie z baz conn.Open() ' tworzymy nowy obiekt SqlDataAdapter przekazujc do niego ' polecenie SQL i otwarte poczenie. Efektem jest dostarczenie ' do niego danych zgodnie z zapytaniem

58

sqlAdapter = New SqlDataAdapter("select * from dbo.klienci", _ conn) ' utworzenie egzemplarza obiektu SqlCommandBuilder przekazujc ' mu obiekt Adapter ' rezultatem jest utworzenie 3 polece wykonujcych Insert, ' Update i Delete Dim cmdBuilder As New SqlCommandBuilder(sqlAdapter) ' wywoanie metody Fill nakazujacej wypenienie pobranymi ' danymi obiektu ds (DataSet) sqlAdapter.Fill(ds) ' okrelamy rdo danych obiektu DataGridView Me.dgvDane.DataSource = ds.Tables(0) ' zerowa kolumnna ma by niewidoczna Me.dgvDane.Columns(0).Visible = False Catch ex As Exception ' wystpi bd, zwracamy komunikat MsgBox("Problem z dostpem do bazy damych") Finally ' ten fragment jest wykonywany zawsze ' zamykamy poczenie conn.Close() ' i zwalniamy zasb conn = Nothing End Try End Sub Private Sub btnUpdate_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles btnUpdate.Click Dim conn As New SqlConnection(strconnSqL) ' deklaracja i utworzenie nowego obiektu typu DataTable Dim dtZmiany As New DataTable Try conn.Open() ' do obiektu DataTable pobieramy zmiany z tabeli o ' indeksie 0 z DataSet dtZmiany = ds.Tables(0).GetChanges ' jeeli byy zmienione rekordy to ... If dtZmiany IsNot Nothing Then ' wywoujemy metod Update obiektu Adapter ' metoda aktualizuje dane w bazie danych wykorzystujc ' obiekt dtZmiany sqlAdapter.Update(dtZmiany) ' komunikat na ekran MsgBox("AKtualizacja tabeli w bazie SQL zrobiona")

59

End If Catch ex As Exception ' ewentualny komunikat o bdzie MsgBox(ex.ToString) Finally 'sprztamy conn.Close() conn = Nothing End Try End Sub End Class

Poniej widok tego formularza w pracy. Pierwszy zrzut ekranowy pokazuje ten formularz w takich rozmiarach, jak zosta zaprojektowany. Efekt jest taki, e w formancie dgvDane zosta uaktywniony pasek poziomego przewijania, poniewa cz informacji (kolumn) pobranych z bazy danych nie jest widoczna.

Moemy za pomoc myszy zmodyfikowa rozmiar tego formularza tak, aby wywietla komplet kolumn (zniknie wtedy pasek poziomego przewijania). Dziki ustawieniu waciwoci Anchor dla formantu dgvDane tak, aby byy zachowane jego odlegoci od wszystkich krawdzi formularza oraz ustawieniu tej samej waciwoci dla przycisku btnUpdate na trzymanie odlegoci wzgldem prawej i dolnej krawdzi formularza jego wygld po zmianie rozmiaru jest dokadnie taki, jakiego oczekujemy.

60

A co by byo, gdybymy nie zmienili waciwoci Anchor dla obu formantw? Konsekwencje pokazane s poniej.

Bez problemy zmieniamy rozmiar formularza, ale rozmiary i pooenie obu formantw pozostay bez zmian. W przypadku dgvDane nic nam nie dao powikszenie rozmiarw formularza, poniewa nie nastpio powieszenie rozmiarw tego formantu (aby zachowa stae, nadane na etapie projektowania, odlegoci od krawdzi formularza). Z kolei przycisk btnUpdate pozosta na swoim miejscu, efekt jest taki, e albo bdzie dla nas niedostpny, albo bdzie nam przeszkadza (wtedy, gdybymy ustawili poprawnie waciwo Anchor dla dgvDane).

61

1.15.2 Update tabeli via procedura przechowywana (klasycznie)


Kolejna pozycja w menu SQL dotyczy wykonania aktualizacji danych w bazie za porednictwem procedury przechowywanej SQL. Dziaanie takie jest zdecydowanie lepsze od wykorzystania moliwoci, jakie daje obiekt CommandBuilder, zwaszcza w sytuacjach, gdy aktualizacja dotyczy tabeli powizanej relacjami z innymi tabelami. Dla zademonstrowania takiego podejcia potrzebna nam bdzie procedura przechowywana SQL realizujca zadanie aktualizacji wskazanego rekordu danej tabeli. Kod takiej procedury o nazwie pUpdateKlienci zosta zaprezentowany w rozdziale 2.1. Procedura pUpdateKlienci jest procedur z parametrami, dla jej wykonania musimy przekaza do obiektu Command w modelu ADO.NET nie tylko nazw procedury, lecz take szereg waciwoci charakteryzujcych poszczeglne parametry. Bd to takie informacje jak nazwa parametru, jego typ, rozmiar, warto oraz kierunek (moe by parametr typu input lub output). Parametry te bd tworzy kolekcj parametrw, ktra bdzie elementem obiektu Command. Wykorzystamy w sensie projektu analogiczny formularz jak w poprzednim rozdziale, tym razem jego nazwa to frmSQLUpdateKlienci. Umieszczono na nim formant typu DataGridView o nazwie dgvDane oraz przycisk polecenie o nazwie btnUpdate. Oba formanty maj, podobnie jak w formularzu frmAdapterSQLCmdB, zmodyfikowan waciwo Anchor w celu umoliwienia swobodnej zmiany rozmiaru formularza. Zasadnicza rnica kryje si w kodzie klasy tego formularza. Poniej wszystkie instrukcje tej klasy wraz ze stosownymi komentarzami.
Imports System.Data Imports System.Data.SqlClient Public Class frmSQLUpdateKlienci ' delaracja potrzebnych staych i zmiennych Const txtKom As String = "Klasyczne wywoanie procedury SQL" Dim sqlAdapter As SqlDataAdapter Dim ds As DataSet ' tablica ta bdzie wykorzystana jako wskanikowa ' w tym sensie, czy mamy robi update czy nie danego rekordu Dim xUpdate() As Boolean Private Sub frmSQLAdapterCmdB_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Load ' deklarujemy i tworzymy instancj obiektu connection Dim conn As New SqlConnection(strconnSqL) ' deklarujemy i tworzymy instacj obiektu Command

62

Dim cmd As New SqlCommand ' deklarujemy i tworzymy instancj obiektu dataset Dim ds As New DataSet Dim i As Integer ' reszta dziaa moe spowodowa bdm std uycie bloku ' Tray-Catch Try conn.Open() ' przypisanie do waciowoci Connection obiektu cmd ' otwartego poczenia cmd.Connection = conn ' okrelenie typu polecenia cmd.CommandType = CommandType.StoredProcedure ' podanie nazwy procedury przechowywanej cmd.CommandText = "dbo.pDajDaneKlientow" ' przygotowania do odebrania danych, ktre zwrci procedura ' deklarujemy obiekt typu DataReader Dim dr As SqlDataReader ' poprzez wywoanie metody ExecuteReader dostarczamy dane ' do DataReader dr = cmd.ExecuteReader ' deklarujemy i tworzymy obiekt typu Dataset Dim dt As New DataTable ' wywoujemy metod Load utworzonego obiektu wskazujc jako ' rdo danych obiekt dr (DataReader) dt.Load(dr) ' tworzymy rdo danych dla obiektu DataGridView Me.dgvDane.DataSource = dt ' ukrywamy zerow kolumn Me.dgvDane.Columns(0).Visible = False ' deklarujemy wymiary tablicy ReDim xUpdate(dt.Rows.Count - 1) ' na wszystkich pozycjach tablicy ustawiamy warto False For i = 0 To UBound(xUpdate) xUpdate(i) = False Next Catch ex As Exception MsgBox("Problem z wykonaniem procedury pDajDaneKlientow") Finally conn.Close() cmd = Nothing conn = Nothing End Try ' ustawienie zmiennej globalnej flaga na True

63

flaga = True End Sub Private Sub btnUpdate_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles btnUpdate.Click Dim conn As New SqlConnection(strconnSqL), i, j As Integer Try conn.Open() ' deklaracja i utworzenie egzemplarza (instancji) ' obiektu Command Dim cmd As New SqlCommand ' przypisanie mu potrzebnych informacji cmd.Connection = conn cmd.CommandType = CommandType.StoredProcedure cmd.CommandText = "dbo.pUpdateKlienci" ' zerujemy kolekcj parametrw cmd.Parameters.Clear() ' deklarujemy nowy parametr Dim param As SqlParameter ' w ptli po wszystkich rekordach badamy, czy jest potrzebna ' aktualizacja For i = 0 To UBound(xUpdate) If xUpdate(i) Then ' jest potrzebna, a wic ... ' tworzymy kolekcj parametrw ' obiekt param staje si nowym parametrem o okrelonej ' nazwie. typie danych i rozmiarze ' rozmiar (ostatni parametr) jest niezerowy dla zmiennych typu String param = New SqlParameter("@idk", SqlDbType.Int, 0) ' do waciwoci Value przypisujemy warto parametru ' pobran z odpowiedniej kolumny i wiersza obiektu typu ' DataGridView. Musimy pamita o tym, ' e rekordy i kolumny s indeksowane od zera param.Value = Me.dgvDane.Rows(i).Cells(0).Value ' okrelamy, czy parametr jest wchodzcy czy wychodzcy param.Direction = ParameterDirection.Input ' dodajemy utworzony parametr do kolekcji parametrw cmd.Parameters.Add(param) ' param = New SqlParameter("@nazwa", _ SqlDbType.NVarChar, 100) param.Value = Me.dgvDane.Rows(i).Cells(1).Value param.Direction = ParameterDirection.Input cmd.Parameters.Add(param)

64

' param = New SqlParameter("@adres", _ SqlDbType.NVarChar, 100) param.Value = Me.dgvDane.Rows(i).Cells(2).Value param.Direction = ParameterDirection.Input cmd.Parameters.Add(param) ' param = New SqlParameter("@nip", SqlDbType.NVarChar, 13) param.Value = Me.dgvDane.Rows(i).Cells(3).Value param.Direction = ParameterDirection.Input cmd.Parameters.Add(param) ' param = New SqlParameter("@mail", SqlDbType.NVarChar, 50) param.Value = Me.dgvDane.Rows(i).Cells(4).Value param.Direction = ParameterDirection.Input cmd.Parameters.Add(param) ' param = New SqlParameter("@telefon", _ SqlDbType.NVarChar, 50) param.Value = Me.dgvDane.Rows(i).Cells(5).Value param.Direction = ParameterDirection.Input cmd.Parameters.Add(param) ' param = New SqlParameter("@osoba", _ SqlDbType.NVarChar, 100) param.Value = Me.dgvDane.Rows(i).Cells(6).Value param.Direction = ParameterDirection.Input cmd.Parameters.Add(param) ' wszystkie parametry dla danego wiersza s ju okrelone ' wywoujemy wic polecenie (metod) ExewcuteNonQuery ' ktra odpowiedzialna jest za wykonanie polecenia ' modyfikujcego cmd.ExecuteNonQuery() End If Next ' wszystkie rekordy wymagajce aktualizacji zostay Zmodyfikowane, sprztamy po sobie param = Nothing cmd = Nothing MsgBox("Aktualizacja zakoczona", MsgBoxStyle.Information, _ txtKom) Catch ex As Exception ' komunikat o bdzie

65

MsgBox("problem z wykonaiem procedury przechowywanej", _ MsgBoxStyle.Critical, txtKom) Finally ' zamykamy i zwalniamy obiekt Connection conn.Close() conn = Nothing End Try End Sub Private Sub dgvDane_CellEndEdit(ByVal sender As Object, _ ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) _ Handles dgvDane.CellEndEdit ' procedura wywoywana jest w momencie zakoczenia edycji dowolnej ' komrki w danym wierszu DataGridView ' jeeli flaga nie jest True to wyjd If Not flaga Then Exit Sub ' konieczna jest zmiana w tabeli xUpdate na pozycji, ktra jest ' Okrelona waciwoci RowIndex argumentu e przekazanego do tej ' procedury z obiektu DataGridView. Inn waciwoci argumentu e ' jest ColumnIndex, w sumie obie waciwoci daj nam ' pen informacj o komrce, ktra zostaa zaktualizowana ' ponisza instrukcja najpierw sprawdza, czy ten wiersz ju nie ' zosta oznaczony jako wymagajcy aktualizacji, jeeli nie, to na ' pozycji e.RowIndex wpisujemy True If Not xUpdate(e.RowIndex) Then xUpdate(e.RowIndex) = True End Sub End Class

Poniej widok tego formularza w trakcie pracy. Po jego otwarciu zosta zmodyfikowany jego rozmiar tak, aby pola (kolumny) Adres oraz OsobaKontaktowa byy w peni widoczne.

W ostatniej kolumnie pierwszego wiersza dopiszemy imiona do istniejcego nazwiska Abacki.

66

Dane zostay uzupenione, symbol owka na pocztku gridu identyfikuje aktualnie modyfikowany rekord (wiersz).

Klik przycisku Aktualizuj uruchamia procedur aktualizujc. Po jej zakoczeniu wywietlany jest stosowny komunikat.

Dla pokazania, e faktycznie zmiany w tabeli Klienci zostay utrwalone formularz zosta zamknity i ponownie otwarty, jak widzimy pierwszy rekord zosta zmodyfikowany.

Z przedstawionego kodu wynika, e taka standardowa (klasyczna) metoda wywoania procedury przechowywanej, zwaszcza z du liczb parametrw, jest do uciliwa. W kolejnym podrozdziale pokazana zostanie rozwizanie, ktre znacznie uatwia tworzenie takiego kodu (obsugujcego wywoanie procedur przechowywanych SQL).

67

1.15.3 Update tabeli via procedura przechowywana wg JG


W tym przykadzie wykorzystamy formularz o nazwie frmSQLUpdateKlienci2, ma on dokadnie taki sam zestaw formantw jak swj poprzednik, ale cakowicie inny kod. Wynika on midzy innymi z tego, e zaprojektujemy klas, z pomoc ktrej bdziemy obsugiwa funkcjonowanie tego formularza. Klasa CTestSql Jest to klasa potomna (dziedziczca) po klasie bazowej CForStorageSub, ktrej kod bdzie omwiony w kolejnym rozdziale. W klasie na potrzeby tego przykadu utworzono jeden interfejs z trzema metodami. Peny kod wraz z komentarzami pokazany jest niej.
Imports System.Data Imports System.Data.SqlClient ' dekalrujemy interfejs klasy Public Interface IKlienci ' ten interfejs bdzie obsugiwa operacje na tabeli Klienci ' deklarujemy funkcj, ktra bdzie zwraca komunikat o ' ewentualnym bdzie Function Komunikat() As String ' dekalrujemy metod odpowiedzialn za pobranie danych z ' tabeli Klienci Sub PrzygotujDaneDoEdycji(ByVal strconn As String, _ ByVal frm As frmSQLUpdateKlienci2) ' deklarujemy metod odpowiedzialn za wykonanie aktualizacji ' danychx Sub WykonajUpdate(ByVal strconn As String, ByVal Xb() As Boolean, _ ByVal dgv As DataGridView) End Interface Public Class CTestSql ' tworzona klasa dziedziczy po klasie bazowej CForStorageSub Inherits CForStorageSub ' kod bdzie implementowa interfejs IKlienci Implements IKlienci ' deklarujemy prywatn zmienn mKomunikat i inicjalizujemy j pustym ' cigiem znakw Private mKomunikat As String = "" Public Function Komunikat() As String Implements IKlienci.Komunikat ' funkcja (metoda) zwraca zmienn prywatn mKomunikat Return mKomunikat End Function

68

Public Sub PrzygotujDaneDoEdycji(ByVal strconn As String, _ ByVal frm As frmSQLUpdateKlienci2) _ Implements IKlienci.PrzygotujDaneDoEdycji ' do metody przekazujemy jako parametry (argumenty) acuch ' poczenia oraz formularz bdcy instancj formularza ' frmSQLUpdateKlienci2 ' ' deklarujemy i tworzymy instancj obiektu Connection Dim conn As New SqlConnection(strconn) ' dekalrujemy obiekt typu DataTable Dim dt As DataTable ' reszta instrukcji w bloku obsugi ewentualnego bdu Try ' otwracie poczenia z baz SQL conn.Open() ' korzystamy z metody klasy bazowej i wywoujemy funkcj ' DajRekordset. Lista publicznych zmiennych, metod i ' waciwoci klasy bazowej jest dostpna po sowie kluczowym ' MyBase i kropce. Wszystkie metody klasy bazowej, ktre ' wykorzystuj pojedyncz procedur przechowywan maj podobny ' zestaw argumentw. Jest to skadnia: ' Nazwa_Metody(obiekt_Connection, nazwa_procedury, ' lista_parametrow) ' jeeli procedura nie ma parametrw, to ostatni skadnik jest ' sowem "brak" ' w tym przypadku przekazujemy obiekt conn, nazw procedury ' wraz z jej prefiksem oraz wyraz "brak" jako sygna, e ' procedura nie ma argumentw. ' Metoda DajRekordset zwraca obiekt typu DataTabel, ktry ' przypisujemy do zmiennej dt dt = MyBase.DajRekordset(conn, "dbo.pDajDaneKlientow", "brak") ' definiujemy teraz rdo danych dla formantu dgvDane frm.dgvDane.DataSource = dt ' ukrywamy kolumn zerow z kluczem tabeli frm.dgvDane.Columns(0).Visible = False Catch ex As Exception mKomunikat = "Problem z wykonaniem procedury pDajDaneKlientow" Finally ' sprztamy po sobie conn.Close() conn = Nothing End Try End Sub

69

Public Sub WykonajUpdate(ByVal strconn As String, ByVal Xb() As _ Boolean, ByVal dgv As DataGridView) _ Implements IKlienci.WykonajUpdate ' ta metoda odpowiada za aktualizacj tych rekordw gridu dgv, ' ktre wymagaj aktualizacji. Informacja o tym, ktre rekordy ' zawarte jest w tablicy Xb Dim conn As New SqlConnection(strconn) Try conn.Open() ' wywoujemy metod UpdateWieleRekordow z klasy bazowej ' przekazujemy do niej argumenty: otwarte poczenie, nazw ' procedury SQL, obiekt typu DataGridView jako rdo rekordw ' do aktualizacji, tablic logiczn oraz zestaw nazw parametrw ' zawierajcych dla kadego z nich nastpujce informacje: ' nazwa parametru, jego typ, rozmiar oraz numer kolumny gridu, ' z ktrej ma by pobrana jego warto MyBase.UpdateWieleRekordow(conn, "dbo.pUpdateKlienci", _ dgv, Xb, _ "@idk", jgTyp.jgInteger, 0, 0, _ "@nazwa", jgTyp.jgString, 100, 1, _ "@adres", jgTyp.jgString, 100, 2, _ "@nip", jgTyp.jgString, 13, 3, _ "@mail", jgTyp.jgString, 50, 4, _ "@telefon", jgTyp.jgString, 50, 5, _ "@osoba", jgTyp.jgString, 100, 6) Catch ex As Exception mKomunikat = "Problem z update wielu rekordw" Finally conn.Close() conn = Nothing End Try End Sub End Class

Kod formularza frmSQLUpdateKlienci2 Fakt utworzenia klasy CTestSql znakomicie skraca i upraszcza kod, ktry musimy utworzy w klasie tego formularza dla zabezpieczenia jego poprawnego funkcjonowania.
Public Class frmSQLUpdateKlienci2 'deklaracja staej i tablicy Const txtKom As String = "Wywoanie procedury SQL wg JG" Dim xUpdate() As Boolean

70

Private Sub frmSQLUpdateKlienci2_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Load 'przedefiniowanie rozmiarw tablicy xUpdate ReDim xUpdate(Me.dgvDane.Rows.Count - 1) ' ustawienie na kadej skadowej wartoci False For i As Integer = 0 To Me.dgvDane.Rows.Count - 1 xUpdate(i) = False Next ' przypisanie wartoci True do zmiennej globalnej flaga ' umoliwi rejestrowanie ewentualnych aktualizacji w gridzie flaga = True End Sub Private Sub btnUpdate_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnUpdate.Click ' deklarujemy obiekt klasy CTestSql wykorzystujc interfejs ' IKlienci Dim w As IKlienci ' tworzymy instancj klasy CTestSql z ogranieczeniem dostpu do ' metod udostpnionych przez interfejs IKlienci w = New CTestSql ' wywoujemy metod WykonajUpdate przekazujc do niej potrzebne ' argumenty, czyli acuch poczenia, tablic xUpdate oraz grid ' dgvDane w.WykonajUpdate(strconnSqL, xUpdate, Me.dgvDane) ' sprawdzamy, czy wszystko przebiego poprawnie If w.Komunikat.Length > 0 Then ' niestety nie, komunikat na ekran, jego tre zostaa ' przygotowana w klasie CTestSql MsgBox(w.Komunikat, MsgBoxStyle.Critical, txtKom) Else MsgBox("Aktualizacja rekordw zakoczona", _ MsgBoxStyle.Information, txtKom) End If End Sub Private Sub dgvDane_CellEndEdit(ByVal sender As Object, _ ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) _ Handles dgvDane.CellEndEdit If Not flaga Then Exit Sub ' jeeli jeszcze nie odnotowano, e wiersz (rekord) o indeksie ' e.RowIndex jest aktualizowany, to ustaw na jego pozycji True If Not xUpdate(e.RowIndex) Then xUpdate(e.RowIndex) = True End Sub End Class

71

Kod uruchamiajcy formularz Formualarz frmSQLUpdateKlienci2 bdzie uruchamiany poprzez klik odpowiedniego polecenia w menu SQL tej aplikacji. Musimy zadba o to, aby formularz by pokazany dopiero wtedy, gdy pomylnie pobrano dane z bazy danych. Jeeli nie, to formularz nie jest pokazywany, a w jego miejsce wywietlamy stosowny komunikat wyjaniajcy sytuacj. Poniej kod procedury obsugujcej klik polecenia mnuUpdateJG z formularza frmMDIForm.
Private Sub mnuUpdateJG_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles mnuUpdateJG.Click Dim frm As New frmSQLUpdateKlienci2 flaga = False ' dekalracja obiektu klasy CTestSql ' z wykorzystaniem interfejsu IKlienci Dim w As IKlienci ' utworzenie instancji klasy CTestSql ' z uwagi na sposb deklaracji dostpne bda ' metody zadekalrowane w interfejsie IKlienci w = New CTestSql ' wywoanie metody PrzygotujDaneDoEdycji w.PrzygotujDaneDoEdycji(strconnSqL, frm) ' sprawdzenie, czy pomylnie pobrano dane z bazy ' i czy zostay przypisane do gridu formularza If w.Komunikat.Length = 0 Then ' tak, pokazujemy formularz ' ale obiekt w nie jest ju potrzebny, zwalniamy zasb w = Nothing frm.MdiParent = Me frm.Show() Else ' nie, komunikat na ekran MsgBox(w.Komunikat, MsgBoxStyle.Critical, _ "Bd przygotowania danych") End If End Sub

Klika zrzutw pokazujcych prac tego formularza Na zakoczenie klika zrzutw pokazujcych formularz w trakcie edycji danych zapisanych w tabeli Klienci. Powiedzmy, e w ostatniej kolumnie drugie imi Kamil dopisano pomykowo w pierwszym rekordzie zamiast w drugim.

72

Poprawiamy bd kasujc Kamil w pierwszym rekordzie i dopisujc to imi w drugim.

Bdy poprawione, wykonujemy klik przycisku Aktualizuj.

Procedura aktualizujca wykonuje swoj prac i wywietla stosowny komunikat. Na zakoczenie zamykamy formularz i ponownie go otwieramy, jak widzimy zmiany s w bazie.

73

1.16 Klasa CForStorageSub


Klasa CForStorageSub zostaa pomylana jako zestaw procedur uatwiajcych dostp do procedur przechowywanych SQL, zwaszcza parametrycznych. Klasa ta bdzie wykorzystywana jako klasa bazowa do projektowania klas potomnych znajdujcych zastosowanie w tworzonych aplikacjach windowsowych. Standardowo klasa ta powinna by wykorzystywana poprzez bibliotek DLL, a nie jawny kod klasy. W takich sytuacjach w aplikacji musimy tylko i wycznie wskaza w referencjach adres pliku CForStorageSub.dll, bez potrzeby doczania do rozwizania penego kodu tej klasy. Na potrzeby tego opracowania w aplikacji ADOAndADONET wczony zosta plik kodu tej klasy, czyli CForStorageSub.vb.

1.16.1 Deklaracja zmiennych i prywatnych procedur klasy


W kodzie klasy CForStorageSub zaimportowano dwie wane przestrzenie nazw, co jest konieczne w przypadku odwoywania si do serwera MS SQL.
Imports System.Data Imports System.Data.SqlClient

Kod klasy rozpoczyna si od zadeklarowania wyliczenia o nazwie jgTyp, jego zadaniem jest umoliwienie symbolicznego okrelania typu parametru. Rozwizanie to ma nam umoliwi okrelenie typu np. parametru tekstowego poprzez warto jgString a nie jej odpowiednik 5. Dodatkowo bdziemy mogli t potrzebn warto wybra z okienka podpowiedzi. Specyfikator dostpu Protected jest po to, aby to wyliczenie byo dostpne w klasie potomnej.
Public Class CForStorageSub ' wyliczenie na potrzeby okrelania typu parametrw Protected Enum jgTyp As Integer jgInteger = 1 jgChar = 2 jgMoney = 3 jgData = 4 jgString = 5 jgBit = 6 jgSingle = 7 End Enum

Prywatna procedura pZbudujParametr odpowiada za zbudowanie parametru i dodanie go do kolekcji parametrw polecenia (obiektu Command). Procedura otrzymuje kolejno obiekt Command, nazw parametru, jego typ, rozmiar oraz opcjonalnie warto parametru.

74

Private Sub pZbudujParametr(ByVal pol As SqlCommand, _ ByVal naz As String, ByVal typ As Integer, _ ByVal roz As Integer, Optional ByVal war As Object = Nothing) Dim prm As New SqlParameter, jaki As Boolean If IsNothing(war) Then jaki = False Else jaki = True End If With prm .ParameterName = naz If jaki Then .Direction = ParameterDirection.Input Else .Direction = ParameterDirection.Output End If Select Case typ Case 1 ' integer .SqlDbType = SqlDbType.Int If jaki Then If IsDBNull(war) Then .Value = war Else .Value = CInt(war) End If End If Case 2 ' znak .SqlDbType = SqlDbType.Char .Size = roz If jaki Then .Value = war Case 3 ' waluta .SqlDbType = SqlDbType.Money If jaki Then If IsDBNull(war) Then .Value = war Else .Value = CDec(war) End If End If Case 4 ' data .SqlDbType = SqlDbType.DateTime If jaki Then If IsDBNull(war) Then .Value = war

75

Else .Value = CDate(war) End If End If Case 5 'varchar .SqlDbType = SqlDbType.NVarChar .Size = roz If jaki Then .Value = war Case 6 'bit .SqlDbType = SqlDbType.Bit If jaki Then If IsDBNull(war) Then .Value = war Else .Value = CBool(war) End If End If Case 7 'single .SqlDbType = SqlDbType.Float If jaki Then If IsDBNull(war) Then .Value = war Else .Value = CSng(war) End If End If End Select End With pol.Parameters.Add(prm) prm = Nothing End Sub

Prywatna funkcja fPrzygotujCmd odpowiada za przygotowanie obiektu Command do wykonania procedury przechowywanej. Jako argumenty otrzymuje obiekt Command (przez referencj), nazw procedury przechowywanej, identyfikator okrelajcy czy procedura zwraca jaki parametr oraz list parametrw z ich waciwociami przekazan jako tablica P() typu Object (typ oglny).
Private Function fPrzygotujCmd(ByRef cmd As SqlCommand, _ ByVal sp_nazwa As String, ByVal out As Boolean, _ ByVal Par() As Object) As Integer Dim i, ip As Integer, u As Integer = 0 cmd.CommandText = sp_nazwa cmd.CommandType = CommandType.StoredProcedure

76

ip = 0 If UBound(Par) > 0 Then If out Then u = 1 For i = 1 To CInt((UBound(Par) + 1) / 4) - u ip = (i - 1) * 4 pZbudujParametr(cmd, CStr(Par(0 + ip)), CInt(Par(2 + ip)), _ CInt(Par(3 + ip)), (Par(1 + ip))) Next i If out Then ip = (i - 1) * 4 pZbudujParametr(cmd, CStr(Par(0 + ip)), CInt(Par(1 + ip)), _ CInt(Par(2 + ip))) End If End If Return ip End Function

1.16.2 Kilka wybranych metod publicznych


Publiczna funkcja DajRekordset odpowiada za wykonanie procedury przechowywanej zwracajcej zestaw rekordw z bazy danych (rekordset). Zwracany jest obiekt typu DataSet. Do funkcji przekazujemy otwarte poczenie, nazw procedury oraz tablic parametrw.
Protected Function DajRekordset(ByVal conn As SqlConnection, _ ByVal sp_nazwa As String, _ ByVal ParamArray Par() As Object) As DataTable Dim cmd As SqlCommand, ip As Integer cmd = New SqlCommand ip = fPrzygotujCmd(cmd, sp_nazwa, False, Par) cmd.Connection = conn Dim dr As SqlDataReader dr = cmd.ExecuteReader Dim dt As New DataTable dt.Load(dr) cmd = Nothing DajRekordset = dt End Function

Publiczna funkcja DajWartosc odpowiada za wykonanie procedury przechowywanej, ktrej zadaniem jest zwrcenie pojedynczej wartoci. Zwracana jest warto typu Object.
Protected Function DajWartosc(ByVal conn As SqlConnection, _ ByVal sp_nazwa As String, _ ByVal ParamArray Par() As Object) As Object

77

Dim cmd As SqlCommand, ip As Integer cmd = New SqlCommand ip = fPrzygotujCmd(cmd, sp_nazwa, True, Par) cmd.Connection = conn cmd.ExecuteNonQuery() DajWartosc = cmd.Parameters(Par(0 + ip).ToString).Value cmd = Nothing End Function

Publiczna funkcja Wykonaj odpowiada za wykonanie procedury przechowywanej typu wykonawczego (czyli Insert, Delete, Update). Zwraca liczb rekordw zmodyfikowanych.
Protected Function Wykonaj(ByVal conn As SqlConnection, _ ByVal sp_nazwa As String, _ ByVal ParamArray Par() As Object) As Integer Dim cmd As SqlCommand, ip As Integer cmd = New SqlCommand ip = fPrzygotujCmd(cmd, sp_nazwa, False, Par) cmd.Connection = conn Wykonaj = cmd.ExecuteNonQuery cmd = Nothing End Function

Publiczna metoda UpdateWieleRekordow zostaa tak zaprojektowana, aby zoptymalizowa aktualizacj wielu rekordw. Zauwaono, e przy aktualizacji wielu rekordw mamy taki sam zestaw parametrw co do ich nazwy, typu oraz rozmiaru. Jedyna zmienna rzecz to ich warto. To spostrzeenie doprowadzio do rozdzielenia etapu tworzenia kolekcji parametrw od przypisywania im okrelonej wartoci.
Protected Sub UpdateWieleRekordow(ByVal conn As SqlConnection, _ ByVal sp_nazwa As String,ByVal dgvZrodlo As DataGridView, _ ByVal KtoreRekordy() As Boolean, ByVal ParamArray Par() As Object) ' macierz Par() dla kadego z parametrw okrela kolejno: nazw ' parametru, jego typ, rozmiar, nr kolumny gridu skd bdzie ' pochodzi warto Dim cmd As SqlCommand, ip, i, j As Integer cmd = New SqlCommand cmd.CommandText = sp_nazwa cmd.CommandType = CommandType.StoredProcedure ip = 0 ' tworzymy kolekcj parametrw (bez wartoci: nazwa, typ, rozmiar For i = 1 To CInt((UBound(Par) + 1) / 4) ip = (i - 1) * 4

78

'tworzymy instancj parametru Dim prm As New SqlParameter With prm .ParameterName = CStr(Par(0 + ip)) .Direction = ParameterDirection.Input .SqlDbType = Choose(Int(Par(1 + ip)), SqlDbType.Int, _ SqlDbType.Char, SqlDbType.Money, SqlDbType.Date, _ SqlDbType.NVarChar, SqlDbType.Bit, SqlDbType.Float) End With ' trzy elementy parametru s okrelone (nie ma wartoci ' (bdzie pniej), dodajemy parametr do kolekcji cmd.Parameters.Add(prm) prm = Nothing Next i ' kolekcja zbudowana, wartoci bd ustawiane w ptli Dim war As Object, k As Integer cmd.Connection = conn For i = 0 To dgvZrodlo.Rows.Count - 1 If KtoreRekordy(i) Then ' potrzebny update ' ustawiamy wartoci poszczeglnych parametrw For j = 0 To cmd.Parameters.Count - 1 k = CInt(Par(j * 4 + 3)) war = dgvZrodlo.Rows(i).Cells(k).Value Select Case Int(Par(j * 4 + 1)) ' w zalenoci od typu ustalamy sposb konwersji jego wartoci Case jgTyp.jgInteger If IsDBNull(war) Then cmd.Parameters(j).Value = war Else cmd.Parameters(j).Value = CInt(war) End If Case jgTyp.jgChar cmd.Parameters(j).Value = war Case jgTyp.jgMoney If IsDBNull(war) Then cmd.Parameters(j).Value = war Else cmd.Parameters(j).Value = CDec(war) End If Case jgTyp.jgData If IsDBNull(war) Then cmd.Parameters(j).Value = war Else cmd.Parameters(j).Value = CDate(war)

79

End If Case jgTyp.jgString cmd.Parameters(j).Value = war Case jgTyp.jgBit If IsDBNull(war) Then cmd.Parameters(j).Value = war Else cmd.Parameters(j).Value = CBool(war) End If Case jgTyp.jgSingle If IsDBNull(war) Then cmd.Parameters(j).Value = war Else cmd.Parameters(j).Value = CSng(war) End If End Select Next ' wartoci parametrw ustawione, wykonujemy procedur cmd.ExecuteNonQuery() End If Next cmd = Nothing End Sub

80

You might also like