Warstwa dostępu do danych
Mainframe
Directory
RDBMS
Email i
wiadomo
ści
System plik
ów
ADO
OLE DB
XML
Problemy
• Modele programistyczne
– Połączeniowy (connected) – oparcie o otwarte
połączenia do systemu bazy danych (OLE DB, ADODB)
– Bezpołączeniowy (disconnected) – pytanie/odpowiedź,
bez-
stanowy między zapytaniami (HTTP)
• Obsługa stanu
• Architektura ADO.NET
• Obiekty DataSet, Connection, Command, DataAdapter i ich
współdziałanie
• Praca w trybie połączonym: DataReader
• Praca w trybie odłączonym: DataSet
• Modyfikacje źródła danych
• Obsługa procedur pamiętanych w b.d.
• Integracja z XML
• Transakcje
• LINQ, Entity Framework
• ADO.NET Data Services
Tryb połączeniowy
• W modelu klient – serwer każdy klient łączy się z bazą podczas startu
aplikacji i zwalnia połączenie podczas jej zamykania
• Serwer musi utrzymywad oddzielne połączenia dla każego klienta
Klienci
Połączenia
Serwer
busy
idle
idle
Możliwe niepotrzebne zużycie
zasobów
Tabele
Wyniki zapytania
Kursor
rs
Klient
Serwer
• Serwery bazodanowe zapewniają
dostęp do kursora
przechowującego stan aktualnego
wiersza
– Dostęp do danych
– Przesuwanie się przez MoveNext
oraz MovePrevious
Tryb połączeniowy
• Połączenie tworzymy tylko raz
• Możemy ustawiać zmienne
powiązane z „sesją‟
• Szeroki dostęp do mechanizmów
zabezpieczajacych dostarczonych
przez bazę danych
• Pierwszy wiersz zapytania dostępny
od razu
• Niepotrzebne zużycie zasobów
• Problemy ze skalowalnością
• Nie dostosowany do aplikacji webowych
• Użytkownicy się nie wylogowują
• Wahająca się liczba użytkowników
• Nie dostosowany do aplikacji
wielowarstwowych
Zalety
Wady
Tryb bezpołączeniowy
•
Połączenia są zwalniane zaraz po wykorzystaniu
•
Obiekty danych wykorzystywane są również po zwolnieniu połączenia
•
Połączenie jest nawiązywane by zapisad zmiany do bazy
Klienci
Połączenia
Serwer
busy
idle
idle
Zasoby są używane tylko gdy są
potrzebne
• Dane są dostarczane do klienta w
jednej operacji
• Wyniki zapytania przechowywane w
pamięci klienta
• Zasoby serwera są zwalniane
• Klient zarządza danymi w trybie off-
line
• Ponowne połączenie z bazą by
zapisać zmiany
Wyniki zapytania
Kursor
Klient
rs
Serwer
Tabele
Tryb bezpołączeniowy
• Mniejsze zużycie zasobów serwera
• Modyfikacja danych jest szybsza i
bardziej elastyczna
• Dane nie związane z połączeniem
• Łatwe przekazywanie między
warstwami
• Wykorzystywane w aplikacjach
wielowarstwowych oraz webowych
• Otwieranie i zamykanie połączeń
jest kosztowne
• Wczytywanie dużych ilości danych
jest czasochłonne
• Zużycie zasobów po stronie klienta
• Mniej opcji zarządzania
bezpieczeńswem
Zalety
Wady
Architektura ADO.NET
Database
DataSet
Tables
DataTable
DataRowCollection
DataColumnCollection
ConstraintCollection
DataRelationCollection
XML
.NET Data Provider
Connection
Transaction
Command
Parameters
DataReader
DataAdapter
SelectCommand
InsertCommand
UpdateCommand
DeleteCommand
ReadXml
WriteXml
Update
Fill
.NET data provider
jest to zestaw klas, za pomocą których podłaczamy się do źródła danych
oraz pobieramy i aktualizujemy dane
ADO.NET Object Model
Klasa
Opis
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)
DataReader
Udostępnia jednokierunkowy (rekord po rekordzie) strumień danych ze źródła, w trybie 'tylko do
odczytu'
DataAdapter
Wypełnia DataSet danymi pochodzącymi ze źródła oraz umożliwia aktualizacje danych w źródle na
podstawie DataSet-
u (kanał łączący obiekt DataSet z dostawcą danych)
Connection
Command
Parameter
DataReader
Transaction
DataAdapter
DataSet
DataTable
DataColumn
DataRow
Constraint
DataRelation
.NET Data
Provider
ADO.NET Class hierarchy
IDbConnection
IDbCommand
IDbTransaction
IDataReader
• Interfejsy
IDbConnection
IDbCommand
IDbTransaction
IDataReader
• Abstrakcyjne klasy bazowe
DbConnection
DbCommand
DbTransaction
DbDataReader
• Implementacja specjalizowana
OleDb: implementacja dla OLEDB
Sql:
implementacja dla SQL
Server
Oracle: implementacja dla Oracle
Odbc: implementacja dla ODBC
DbConnection
DbCommand
DbTransaction
DbDataReader
OleDbConnection
SqlConnection
OracleConnection
...
OleDbCommand
SqlCommand
OracleCommand
OleDbTransaction
SqlTransaction
OracleTransaction
OleDbDataReader
SqlDataReader
OracleDataReader
Tworzenie połączenia
• SqlConnection
• Parametry ConnectionString
– Connection timeout: dopuszczalny czas
uzyskania połączenia
– Data source: nazwa instancji SQL Server lub
nazwa komputera
– Initial catalog: nazwa bazy danych
– Integrated security; gdy True połączenie z SQL
serwerem na podstawie tożsamości konta procesu
ASP.NET
– User ID: konto logowania SQL Server
– Password:
– …
string
strConn
= "data source=localhost; " +
"initial catalog=northwind; integrated security=true";
SqlConnection
conn
= new SqlConnection(
strConn
);
http://www.connectionstrings.com
/
• Łańcuch połączenia (ang. connection string) - ciąg znaków zawierających
parametry konfiguracji połączenia
Połączenie: Tworzenie połączenia
public
interface
IDbConnection
{
string
ConnectionString {
get
;
set
;}
int
ConnectionTimeout {
get
;}
string
Database {
get
;}
ConnectionState State {
get
;}
IDbTransaction BeginTransaction();
IDbTransaction BeginTransaction(IsolationLevel il);
void
ChangeDatabase(
string
db);
void
Close();
IDbCommand CreateCommand();
void
Open();
}
BeginTransaction
–
Rozpoczyna tranzakcję
Open, Close
– Otwieranie i zamykanie
połączenia
CreateCommand
– Tworzy obiekt Command
powiązany z połączeniem
ConnectionTimeout
– Określenie czasu timeoutu
połączenia
Database
– Nazwa bazy dla połączenia
State
– Zwraca stan aktualnego połączenia: Broken,
Closed, Connecting, Executing, Fetching, or Open.
ChangeDatabase
– Zmiana
bazy dla otrawtego połącznia
ConnectionString - zarządzanie
• Umieszczamy w sekcji <connectionStrings> pliku konfiguracyjnego
< configuration >
...
< connectionStrings >
< add
name
=”
Northwind
” providerName=”System.Data.SqlClient”
connectionString=”server=(local);
integrated security=SSPI;database=Northwind” / >
< /connectionStrings
>
< /configuration >
private DbConnection
GetDatabaseConnection
( string
name
)
{
ConnectionStringSettings settings =
ConfigurationManager.ConnectionStrings[
name
];
DbProviderFactory factory =
DbProviderFactories.GetFactory(settings.ProviderName );
DbConnection
conn
= factory.CreateConnection ( ) ;
conn.ConnectionString = settings.ConnectionString ;
return
conn
;
}
Wydajne używanie połączeo
try . . . catch . . . finally
try
{
// Open the connection
conn.Open();
DoSomething();
}
catch
( SqlException ex )
{
//Log the exception
}
finally
{
conn.Close ( ) ;
}
Blok using
try
{
using
(SqlConnection conn =
new SqlConnection(source))
{
// Open the connection
conn.Open ( ) ;
DoSomething();
}
}
catch
(SqlException e)
{
// Log the exception
}
using
– zapewnia wywołanie Dispose()
najwcześniej jak to możliwe, poprzez
zdeklarowanie używanych obiektów i określeniu
przez nawiasy {} zasięgu tych obiektów
Pula połączeo
• Connection pooling
– proces utrzymywania otwartych połączeń i
ponownego ich reużycia dla uzytkownika lub kontekstu
• Parametry ConnectionString dla poolingu
– Connection Lifetime: długość oczekiwania połączenia na ponowne użycie
– Max Pool Size: maksymalna liczba połączeń
– Min Pool Size: Minimalna liczba połączeń
– Pooling: True/False
– …
cnNorthwind.
ConnectionString
= _
"Integrated Security=True;" & _
"Initial Catalog=Northwind;" & _
"Data Source=London;" & _
"Pooling=True;" & _
"Min Pool Size=5;" & _
"Connection Lifetime=120;"
ADO.NET – modele programowania
• ADO.NET zapewnia dostęp do obu typów
programowania
– Połączeniowy
• Używa obiektów
Command
i
DataReader
• DataReader
służy do odczytu w przód
• Zmiany/aktualizacje odbywają się przez obiekt
Command
– Bezpołączeniowy
• Używa obiektów
DataSet
do przechowywania danych u klienta
• DataAdapter
obsługuje komunikację miedzy obiektem
DataSet
a
serwerem
• Obiekty DataSet są niezależne od providera
• Obiekty DataSet są przechowywane oraz przesyłane przez XML
Obiekt Command
• Parameters
– Parametry, z którymi zostanie
wykonane zapytanie
public
interface
IDbCommand
{
string
CommandText
{
get
;
set
;}
int
CommandTimeout {
get
;
set
;}
CommandType CommandType {
get
;
set
;}
IDbConnection
Connection
{
get
;
set
;}
IDbTransaction Transaction {
get
;
set
;}
UpdateRowSource UpdatedRowSource {
get
;
set
;}
IDataParameterCollection Parameters {
get
;}
void
Cancel();
IDataParameter CreateParameter();
int
ExecuteNonQuery();
IDataReader ExecuteReader();
IDataReader ExecuteReader(CommandBehavior cb);
object
ExecuteScalar();
void
Prepare();
// Note
ExecuteXmlReader (SqlCommand only)
}
• Connection
- referencja do obiektu
połączenia
• CommandType - typ polecenia
– Text – wyrażenie SQL
– StoredProcedure
• CommandText
- w
zależności od
wyboru typu plecenia:
– Text – treść polecenia SQL
– StoredProcedure – nazwa procedury
Metody wywołania obiektu Command
•
Zapytanie nie zwraca wierszy
– Wywołanie
ExecuteNonQuery
• Zwraca liczbę wierszy ‘dotkniętych’ przez zapytanie
– Zapytania DDL and DCL
• CREATE, ALTER, DROP, GRANT, DENY, REVOKE
– Zapytaia DML
• INSERT, UPDATE, DELETE
•
Zapytanie zwraca pojedynczą wartośd
– Wywołanie
ExecuteScalar
• ExecuteScalar zwraca typ Object
•
Zapytanie zwraca wiersz
– Metoda
ExecuteReader
• Zwraca obiekt
DataReader
• Reader zależny od providera: SqlDataReader, OleDbDataReader
– DataReader
• Służy tylko do odczytu, możliwe przesuwanie tylko w przód
•
Zapytanie zwraca XML
– ExecuteXmlReader
– dostępny tylko dla SQL Server
Command - przykład
private
void
Demo()
{
SqlConnection
con
=
new
SqlConnection(
"Server=localhost; Database=Pubs; Integrated Security=SSPI" );
SqlCommand cmd =
new
SqlCommand(
"SELECT COUNT( * ) FROM Authors",
con
);
con
.Open();
Console.WriteLine( cmd.
ExecuteScalar
() );
// Writes '23'
con
.Close();
// Important!
}
Asynchroniczne wywołanie Command
• ADO.NET 2.0 wspiera tryb asynchroniczny
• Wykonanie poleceń w tle
IAsyncResult BeginExecuteReader (AsyncCallback callback)
IDataReader EndExecuteReader (AsyncResult result)
IAsyncResult BeginExecuteNonQuery (AsyncCallback callback)
int
EndExecuteNonQuery (IAsyncResult result)
IAsyncResult BeginExecuteXmlReader (AsyncCallback callback)
IDataReader EndExecuteXmlReader (IAsyncResult result)
Asynchroniczny Command - przykład
...
public class
Async {
SqlCommand cmd;
//---- command which should be executed asynchronously
public void
CallCmdAsync() {
SqlConnection conn =
new
SqlConnection(
"Data Source=(local)\\NetSDK...; Asynchronous Processing=true"
)
;
cmd = new SqlCommand("MyLongRunningStoredProc", conn);
cmd.CommandType = CommandType.StoredProcedure;
conn.Open();
//---- start asynchronous execution of command
cmd.
BeginExecuteNonQuery
(new AsyncCallback(AsyncCmdEnded), null);
...
}
//---- callback method called at the end of the execution of command
public void
AsyncCmdEnded(IAsyncResult result) {
//---- process result of command
int
affectedRows = cmd.
EndExecuteNonQuery
(result);
...
}
}
Callback when query is finished
Zapytania parametryzowane
• Command pozwala na definiowanie
parametrów wejściowych i wyjściowych
• Parameter - pola
– Name: nazwa parametru
– Value: wartość parametru
– DbDataType: typ danych
– Direction: kierunek parametru
- Input
- Output
- InputOutput
- ReturnValue
<<interface>>
IDbCommand
<<interface>>
IDataParameter
<<interface>>
IDbDataParameter
//----- Properties
DbType DbType {get; set;}
ParameterDirection Direction {get; set;}
string ParamterName {get; set;}
object Value {get; set;}
...
//----- Properties
int Size {get; set;}
...
Parameters
*
...
IDataParameterCollection
Parameters {get;}
...
IDataParameterCollection Parameters {
get
;}
Zapytania parametryzowane - przykład
2. Dodanie parametru
cmd.Parameters.Add(
new
OleDbParameter
("@ID"
, OleDbType.BigInt));
SqlCommand cmd =
new
SqlCommand();
cmd.CommandText =
"DELETE FROM Empls WHERE EmployeeID = @ID"
;
1.
Zdefiniowanie zapytania
SQL Server:
Identyfikacja parametru przez „@”(przykład: "@name")
3.
Przypisanie wartości
cmd.Parameters[
"@ID"
].Value = 1234;
cmd.ExecuteNonQuery();
Tryb połączeniowy: DataReader
• Tylko do odczytu w przód
• Szybki dostęp
• Praca w trybie połączeniowym
• Programista zarządza połączeniem i danymi
• Małe zużycie zasobów
Użycie DataReader:
1.
Stwórz i otwórz połączenie
2.
Stwórz obiekt Command
3.
Stwórz DataReader dla obiektu Command
4. Odczytaj dane
5. Zamknij DataReader
6.
Zamknij połączenie
Użycie w bloku Try…Catch…Finally
public
interface
IDataReader
{
int
Depth {
get
;}
bool
IsClosed {
get
;}
int
RecordsAffected {
get
;}
…
void
Close();
DataTable GetSchemaTable();
bool
NextResult();
bool
Read();
…
}
DataReader
– Odczyt strumienia danych zwróconych przez zapytanie
NextResult -
Przejście do kolejnego zestawu wyników
Read
– Przechodzi do następnego rekordu
Praca z IDataReader - przykład
object
[ ] dataRow =
new
object[reader.FieldCount];
int
cols = reader.GetValues(dataRow);
• Przeczytaj kolumny do tablicy
object
val0 = reader[0];
object
nameVal = reader[
"LastName"
];
• Odczyt za pomocą indekserów
string
firstName = reader.getString(2);
• Odczyt za pomocą metod
}
reader.Close();
• Zamknięcie obiektu
IDataReader reader = cmd.ExecuteReader();
while
(reader.Read()) {
• Stwórz obiekt i rozpocznij odczyt
Odczyt danych z obiektu DataReader
• Wołanie Read dla każdego rekordu
– Zwraca false gdy brak danych
• Dostęp do pól
– Dostęp poprzez indeks lub nazwę
– Funkcje Get… - najlepsza
wydajność
• Zamknięcie DataReader
• Zamkniecie połączenia
while (myReader.Read())
{
str += myReader[1];
str += myReader["field"];
str += myReader.
Get
DateTime(2);
}
// Open Connection and create command
SqlConnection
conn
= new
SqlConnection("data source=localhost;
initial catalog=pubs; integrated
security=true");
SqlCommand cmdAuthors = new
SqlCommand("select * from Authors",
conn
);
conn.Open();
// Create DataReader and read data
SqlDataReader
dr
;
dr = cmdAuthors.
ExecuteReader
();
while (dr.
Read
())
{
lstBuiltNames.Items.Add(dr["au_lname"] + ",
" + dr["au_fname"]);
}
// Close DataReader and Connection
dr.
Close
();
conn.Close();
Transakcje
• Transakcje lokalne
– Dostęp z jednego połączenia
– Udostępnione przez ADO.NET
• Transakcje rozproszone
– Wykonywane na wielu połączniacj
– Użycie Microsoft Distributed Transaction Component
(MSDTC)
– namespace System.Transaction
Transkacje lokalne
• ADO.NET wspiera tranzakcyjność
– Transakcję rozpoczynamy poprzez metodę
BeginTransaction
która zwraca obiekt
transakcji.
– Transakcja wspiera metody wykonujące
zmiany na bazie danych
– polecenia
(Command)
• Transakcja jest
– Zatwierdzana poprzez
Commit
– Wycofywana poprzez
Rollback
<<interface>>
IDbCommand
<<interface>>
IDbTransaction
<<interface>>
IDbConnection
//----- Properties
IDbConnection Connection {
get
;}
IsolationLevel IsolationLevel {
get
;}
// Methods
void
Commit();
void
Rollback();
...
Transaction 1
...
IDbTransaction Transaction {
get
;
set
;}
...
Connection 1
…
IDbTransaction BeginTransaction();
IDbTransaction BeginTransaction
(IsolationLevel lvl);
Transakcje - przykład
2.
Stworzenie obiektów
IDbCommand cmd1 = con.CreateCommand();
cmd1.CommandText =
"DELETE [OrderDetails] WHERE OrderId = 10258"
;
cmd1.Transaction =
trans
;
cmd1.ExecuteNonQuery();
IDbCommand cmd2 = con.CreateCommand();
cmd2.CommandText =
"DELETE Orders WHERE OrderId = 10258"
;
cmd2.Transaction =
trans
;
cmd2.ExecuteNonQuery();
SqlConnection con =
new
SqlConnection(connStr);
IDbTranaction trans =
null
;
try
{
con.Open();
trans
= con.
BeginTransaction
(IsolationLevel.ReadCommitted);
1. Definicja
trans
.Commit();
catch
(Exception e) {
if
(trans != null)
trans
.Rollback();
}
finally
{
try
{
con.Close();
}
}
3.
Zatwierdzenie lub cofnięcie wyników
Poziomy izolacji
•
Definiują blokady na odczyt i zapis
•
ADO.NET
zapewnia różne poziomy izolacji
ReadUncommitted
• Dostęp do zablokowanych danych
• Dirty reads
ReadCommitted
• Odczyt zablokowanych wierszy zabroniony
• Brak dirty read, może wystąpić phantom row
• Non-repeatable reads
RepeatableRead
• ReadCommitted bez non-repeatable reads
Serializable
• Dostęp w seriach
• Najwyższy poziom izolacji
• Nie występują phantom rows
public enum
IsolationLevel {
ReadUncommitted, ReadCommitted, RepeatableRead, Serializable, ...
}
2.3.3 Tryb bezpołączeniowy: use DataSet
Database
4.
Zwraca
DataSet
do klienta
5.
Zmiana danych przez użytkownika
2.
Tworzenie obiektów SqlConnection i SqlDataAdapter
3.
Wypełnienie
DataSet
z DataAdapter i
zamknięcie połączenia
SqlDataAdapter
SqlConnection
List-Bound
Control
1.
Klient tworzy żądanie wyświetlenia strony
1
2
3
4
5
6.
Aktualizacja danych
DataSet
7.
Użycie SqlDataAdapter do otwarcia
połączenia SqlConnection, aktualizacja
danych w bazie i zamknięcie połączenia
6
7
Client
Web server
DataSet
Czym jest DataSet?
• DataSet zawiera wiele
DataTables
• Relacje są reprezentowane
poprzez DataRelation
• Można wymusić Constrainst
• Dostęp do danych w tabelach
poprzez DataRow i
DataColumn
DataTable
DataColumn
DataRow
DataRelation
Tables
DataTable
Relations
DataRelation
DataRelation
DataRow(s)
DataColumn
Constraint(s)
DataTable
DataTable
DataViewManager
DataView
DataSet
– baza danych utrzymywana w pamięci(struktuwa relacyjna)
Stosowanie obiektu DataSet
• gdy dane muszą byd edytowane lub gdy do bazy trzeba dodawad
i usuwad rekordy.
• gdy zachodzi potrzeba organizowania danych - filtrowania, sortowania
czy wyszukiwania
• gdy rekordy pobrane z bazy danych będą przetwarzane w wielu
iteracjach
• gdy wynikowy zbiór danych pomiędzy kolejnymi odwołaniami do tej
samej strony musi zostad zachowany w obiekcie Session lub Cache.
• do przekazywania wyników działania obiektów warstwy biznesowej
i usług Web Service
– odłączony obiekt DataSet może byd serializowany do postaci XML i przesyłany z
wykorzystaniem protokołu HTTP
DataSet vs. DataReader
DataSet
DataReader
Operacje odczytu i zapisu
Tylko do odczytu
Wiele tabel z różnych źródeł
Oparty o jedno polecenie SQL
Bezpołączeniowy
Połączeniowy
Źródło dla wielu kontrolek
Źródło dla jednej kontrolki
Przesuwanie się w przód i tył
Przesuwanie tylko do przodu
Wolniejszy dostęp
Szybszy dostęp
Wspierany przez narzędzia automatyzujące pracę
Wymaga ręcznej implementacji
Diagram klasy DataSet
DataRow
//----- Properties
string
DataSetName {
get
;
set
;
}
DataRelationsCollection Relations {
get
;}
DataTableCollection Tables {
get
;}
PropertyCollection ExtendedProperties {
get
;}
string
NameSpace {
get
;}
bool
HasErrors {
get
;}
...
*
Rows
DataSet
Transaction 1
//----- Properties
string
DataSetName {
get
;
set
;}
DataRelationsCollection Relations {
get
;}
DataTableCollection Tables {
get
;}
PropertyCollection ExtendedProperties {
get
;}
string
NameSpace {
get
;}
bool
HasErrors {
get
;}
...
DataTable
//----- Properties
string
TableName {
get
;}
DataRelationsCollection ChildRelations {
get
;}
DataRelationsCollection ParentRelations {
get
;}
ConstraintCollection Constraints {
get
;}
DataColumnCollection Columns {
get
;}
DataView DefaultView {
get
;}
bool
HasErrors {
get
;}
DataColumn[] PrimaryKey{
get
;
set
;}
DataRowCollection Rows {
get
;}
...
* Tables
DataRelation
//----- Properties
string
RelationName {
get
;
set
;}
DataTable ParentTable {
get
;}
DataColumn[] ParentColumns {
get
;}
DataTable ChildTable {
get
;}
DataColumn[] ChildColumns {
get
;}
...
Relations
*
ParentRelations
*
ChildRelations
*
*
ParentColumns
*
ChildColumns
DataColumn
//----- Properties
bool
AllowDBNull {
get
;
set
;}
bool
AutoIncrement {
get
;
set
;}
int
AutoIncrementSeed {
get
;
set
;}
int
AutoIncrementStep {
get
;
set
;}
string
ColumnName {
get
;
set
;}
Type DataType {
get
;
set
;}
string
Expression {
get
;
set
;}
bool
ReadOnly {
get
;
set
;}
bool
Unique {
get
;
set
;}
...
Columns
*
Klasa DataSet
Dostęp do:
•
DataTable
– DataSet.Tables[0]
– DataSet.Tables*“tablename”+
•
DataColumn
– DataTable.Columns[0]
– DataTable.Columns*“columnname”+
•
DataRow
– DataTable.Rows[0]
•
Pola tabeli
– DataRow[0]
– DataRow*“columnname”+
• DataSet
składa się z
– Kolecji DataTable
– Kolekcji DataRelation
• DataTable
składa się z
– collection of DataTableColumns
(= schema definition)
– collection of DataTableRows
(= data)
– DefaultView (DataTableView)
• DataRelation
– Łączy dwa obiekty DataTable
– definiujue ParentTable i
ParentColumns
oraz ChildTable i ChildColumns
Typowany i nietypowany DataSet
•
Typowany dataset
jest datasetem, który dziedziczony jest z bazowej klasy DataSet, a
następnie na podstawie informacji z bazy danych generuje nową klasę. Informacje o typach
(tabele, kolumny, relacje, …) są dołączane do obiektu.
– VS daje możliwośd automatyczniej generacji typowanych datasetów
– Może zostad zbudowany w oparciu o dane z pliku XSD
– Mniej podatne na błędy
– Mniejsza elastycznośd
•
Nietypowany dataset
nie posiada wbudowanego schematu. Zawiera również tabele, wiersze,
kolumny, jednak są one udostępniane jako zwykłe kolekcje
– Rozwiązanie bardziej elastyczne, umożliwia pracę z nieznannym źródłem
•
Porównanie:
– Poprawne dla obu typów:
MyDataSet.Tables[“News”].Rows[0][“Title”]
– Poprawne tylko dla typowanego DataSet + zwraca dane we właściwym formacie
MyDataSet.News[0].Title
Zdarzenia w DataTable
• Podział na trzy kategorie:
• Dla kolumn: ColumnChanging, ColumnChanged
– DataColumnChangeEventsArgs: Column, ProposedValue, Row
• Dla wierszy: RowChanging, RowChanged, RowDeleting,
RowDeleted
– DataRowChangeEventArgs: Action (Add, Change,
ChangeCurrentAndOriginal, ChangeOriginal, Commit, Delete,
Nothing, Rollback), Row
• Dla tabel: TableClearing, TableCleared, TableNewRow
– DataTableClearEventArgs: Table, TableName, TableNamespace
– DataTableNewRowEventArgs key member: Row
Wypełnianie DataSet: DataAdapter
DataAdapter
s
łuży jako most pomiędzy DataSetem a źródłem danych pozwalający
na wymianę danych. DataAdapter reprezentuje zestaw poleceń oraz połączenie
bazodanowe które są uzywane do wypełnia DataSet oraz aktualizacji bazy. Dane są
wymieniane poprzez zapytania SQL lub procedury składowane.
• DataAdapter - właściwości:
– SelectCommand – odczytuje dane ze źródła
– InsertCommand – zapisuje dane z DataSet do bazy
– UpdateCommand – aktualizuje dane w bazie danymi z DataSet
– DeleteCommand – usuwa dane z DataSer
• DataAdapter – metody:
– Fill
– odświeża DataSet danymi z bazy (używa SELECT)
– Update
– przenosi zmiany z DataSet do bazy (używa INSERT, UPDATE, DELETE)
DataAdapter - polecenia
• Tworzone w trzy sposoby
– Użycie obiektu
CommandBuilder
by stworzyd Command
podczas wykonania
• Proste do realizacji, narzut na wykonanie
• Ograniczenie do Select dla jednej tabeli
– Poprzez
Visual Studio
w trakcie tworzenia aplikacji
• Proste do realizacji, brak narzutu na wykonanie
• Ograniczenie do Select dla jednej tabeli
– Stworzenie programowo podczas tworzenia aplikacji
• Wysoka kontrola i wydajnośd
• Brak ograniczeo
• Narzut na czas implementacji
CommandBuilder
DataTable dt= ds.Tables["movies"];
// Use command builder to generate update commands
SqlCommandBuilder
sb
= new
SqlCommandBuilde
r(
da
);
// Add movie to table
DataRow drow = dt.NewRow();
drow["movie_Title"] = "Taxi Driver";
drow["movie_Year"] = "1976";
dt.Rows.Add(drow);
// Delete row from table
dt.Rows[4].Delete();
// Edit Column value
dt.Rows[5]["movie_Year"] = "1944";
// Update underlying Sql Server table
int updates =
da
.Update(ds, "movies");
MessageBox.Show("Rows Changed: " +updates.ToString());
Obiekt CommandBuilder
generuje polecenia wymagane do aktualizacji źródła
danych po wprowadzeniu zmian w obiekcie DataSet.
Ograniczenia:
• polecenie Select dotyczy pojedynczej tabeli
• tabela w bazie musi mieć klucz główny lub unikatową kolumnę zawartą w oryginalnym
poleceniu Select
Data Adapter - tworzenie
• Zapisanie zapytania w
DataAdapter
• Konstruktor ustawia wartość
SelectCommand
• Gdy wymagane, utworzenie
InsertCommand
,
UpdateCommand
,
DeleteCommand
SqlDataAdapter da = new SqlDataAdapter
("select * from Authors",
conn
);
da.SelectCommand.CommandText;
da.SelectCommand.Connection;
DataSet - tworzenie
private
void
Demo()
{
SqlDataAdapter
da
=
new
SqlDataAdapter(
"SELECT City FROM Authors",
"Server=localhost; Database=Pubs; Integrated Security=SSPI" );
DataSet ds =
new
DataSet();
da
.
Fill
( ds, "Authors" );
// Opens and closes a connection
foreach
( DataRow dr
in
ds.Tables[ "Authors" ].Rows )
Console.WriteLine( dr[ "City" ] );
// Writes list of cities
}
• Ładowanie danych poprzez SelectCommand obiektu DataAdapter
– Definicja SQL, przez który zostaną załadowane dane
– SelectCommand jako konstruktor
DataSet - tworzenie
• Tworzenie i załadowanie danymi DataTable
– Fill
wywołuje
SelectCommand
obiektu
DataAdapter
• Dostęp do DataTable
DataSet ds = new DataSet();
da.
Fill
(ds, "Authors");
ds.Tables["Authors"].Rows.Count;
string str="";
foreach(DataRow r in
ds.Tables["Authors"].Rows)
{
str += r[2];
str += r["au_lname"];
}
Wypełnianie DataSet
Wydajność
• Zdefiniowanie schematu przed wypełnieniem DataSet
– DataTables, DataColumns, DataRelations są znane przed załadowaniem danych
– Zwiększenie wydajności
• Tworzenie typowanych DataSet
:
- dsCustomers.Customers.BeginLoadData();
- daCustomers.Fill(dsCustomers.Customers);
- dsCustomers.Customers.EndLoadData();
- dataGrid1.DataSource = dsCustomers.Customers.DefaultView;;
Dane z wielu DataAdapter
• DataSet może przechowywać dane z wielu obiektów DataAdapter
– 1 DataAdapter = 1 DataTable
• Wywołanie metody Fill
– Określenie tabeli
- daCustomers.Fill(dsCustomerOrders.Customers);
- daOrders.Fill(dsCustomerOrders.Orders);
- dataGrid1.DataSource = dsCustomerOrders.Customers.DefaultView;
DataSet - podsumowanie
• DataSet może:
– Przechowywad dane w wielu powiązanych tabelach
– Modelowad zależności między tabelami
– Zarządza constrainami
– Daje dostęp do widoków celem bardziej efektywnego
wyświetlania danych
– Byd przesyłany pomiędzy procesami i warstwami
• DataSet i XML:
– XML może zostad załadowany do DataSet
– DataSet może zostad przesłany jako XML
– DataSet może wczytywad xsd
DataView
• DataView służy modyfikowaniu DataTable celem wyświetlenia potrzebych
danych
• DefaultView zwraca standardowy widok dla DataTable
• Modyfikacja widoku z DataSet poprzez filtry
DataView dv = new DataView(ds.Tables["Authors"]);
dv.
RowFilter
= "state = 'CA'";
DataView dv = ds.Tables["Authors"].DefaultView;
Trzy parametry opcjonalne
• Filter,
"City='London'"
• Sort
, "CompanyName ASC"
• DataViewRowState
Relacje
•
Kolumna rodzica
•
Kolumna dziecka
•
Stworzenie relacji
dr = New DataRelation _
(„CustOrders", parentCol, _
childCol)
ds.DataRelations.Add(dr)
DataColumn parentCol = ds.Tables["Customers
"
].Columns["CustomerID
"
]
childCol = ds.Tables["Orders
"
].Columns["CustomerID
"
]
Orders table
Customers table
DataSet
parentCol
childCol
DataRelation
DataRelation
definiuje relację na potrzeby nawigacji
Nawigacja poprzez relacje
Customers
Orders
GetChildRows
GetParentRow
DataSet
ds.Tables[index].Rows[index].
GetChildRows
("relation");
ds.Tables[index].Rows[index].
GetParentRow
("relation");
DataView tableView;
DataRowView currentRowView;
tableView = new DataView(ds.Tables["Customers"]);
currentRowView
= tableView[dgCustomers.SelectedIndex];
dgChild.DataSource =
currentRowView
.CreateChildView("CustOrders");
Customers
Orders
CreateChildView
DataRowView
DataView
DataSet
Modyfikacja danych w DataTable
• BeginEdit rozpoczyna edycję danych
• EndEdit i CancelEdit kończą edycję danych
Wstawianie wiersza
• Stworzenie wiersza
DataRow
drNewEmployee
= dtEmployees.
NewRow
()
• Wypełnienie danymi
drNewEmployee
("EmployeeID") = 11
drNewEmployee
("LastName") = "Smith"
• Dodanie do DataTable
dtEmployees.Rows.
Add
(
drNewEmployee
)
• Jednowierszowo
dtEmployees.Rows.
Add
( New Object() {11, "Smith"})
DataRos drEmployee =
dtEmployees.Rows(3)
drEmployee.
BeginEdit()
drEmployee("FirstName") = "John"
drEmployee("LastName") = "Smith"
drEmployee.
EndEdit()
Modyfikacja danych w DataTable
Usuwanie wiersza
• Metoda
Remove
– Usuwa wiersz z kolekcji
– Przykład:
dtEmployees.Rows.
Remove
(drEmployee)
• Metoda
Delete
klasy DataRow
– Oznacza wiersz jako usunięty
– Wiersz staje się „ukryty”, możemy uzyskać do niego dostęp
– Przykład:
drEmployee.
Delete()
Śledzenie zmian w DataSet
• DataRow może przechowywad wiele wersji wiersza:
– DataRowVersion.
Current
• Aktualna wartośd
– DataRowVersion.
Original
• Wartośd przed dokonaniem zmian
– DataRowVersion.
Proposed
• Wartośd w trakcie cyklu BeginEdit / EndEdit
– DataRowVersion.
Default
• Wartośd standardowa
RowVersion - stany
CURRENT
ORIGINAL
PROPOSED
White
White
N/A
White
White
Brown
Brown
White
N/A
dataRow.BeginEdit();
dataRow[ "au_lname" ]
= "Brown";
dataRow.EndEdit();
dataRow[ "au_lname", DataRowVersion.Current ] // Brown
dataRow[ "au_lname", DataRowVersion.Original ] // White
Diagram stanów obiektu DataRow
•
DataRow
może być w różnych stanach
public
DataRowState RowState {get;}
public enum
DataRowState {
Added, Deleted, Detached, Modified, Unchanged
}
Detached
Deleted
Unchanged
Modified
row =table.NewRow
table.Row.
Add(row)
Reject-
Changes
row.
Delete
Accept-
Changes
row.Delete
RejectChanges
row.Delete
Accept-
Changes
Accept-
Changes
Reject-
Changes
row[..] = ..
table.Rows.
Remove(row)
Added
Modyfikacja źródła danych
• Modyfikacja źródła danych przez DataAdapter
– InsertCommand, UpdateCommand, DeleteCommand
RowState = Modified
RowState = Unchanged
RowState = Added
RowState = Modified
RowState = Deleted
Use UPDATE command
Ignore
Use INSERT command
Use UPDATE command
Use DELETE command
DataRows in DataTable
• Modyfikacje są zapisywane poprzez metodę Update obiektu DataAdapter
– DataAdapter przeszukuje wiersze pod kątem RowState
– Wykonuje akcję zgodnie ze stanem wiersza
Optymalizacja zmian
• DataSet oraz DataTable wspierają metodę
GetChanges
– GetChanges bez argumentów
• Pobiera wiersze, których RowState jest inny niż Unchanged
• Wykorzystanie podczas przekazywania między warstwami
dsChanges = ds.
GetChanges
();
– GetChanges z argumentem RowState
• Wiersze, które mają określony RowState
• Pozwala zarządzad kolejnością aktualizacji
changes = ds.
GetChanges
( DataRowState.Added );
• Medota Merge pozwala na scalenie danych
Obsługa współbieżnego dostępu do danych
• Tryb bezpołączeniowy używa podejścia optymistycznego (optimistic
concurrency)
–
Zwalnianie blokad podczas rozłączania
• Możliwość konfliktów
–
Dane mogły zostać zmienione
-
Usunięcie wiersza
-
Zmiana wartości w polu wiersza
Wykrywanie konfliktów
• Data Adapter Configuration Wizard pozwala generować zapytania SQL
wykrywające konflikty
• Podczas aktualizacji:
–
Porównanie bieżących wartości z oryginalnymi
(where …)
–
Różnice powodują konflikt
• Dodanie do tabeli pola timestamp - aktualizacja pola przy zmianie wartości.
http://msdn.microsoft.com/en-us/library/aa0416cz.aspx
http://msdn.microsoft.com/msdnmag/issues/04/09/DataPoints
Naruszenie spójności danych
Column name
Original value
Current value
Value in database
CustID
101
101
101
LastName
Smith
Smith
Smith
FirstName
Bob
Bob
Bob
Column name
Original value
Current value
Value in database
CustID
101
101
101
LastName
Smith
Smith
Smith
FirstName
Bob
Robert
Bob
Column name
Original value
Current value
Value in database
CustID
101
101
101
LastName
Smith
Smith
Smith
FirstName
Bob
James
Robert
User1 czyta wiersz
User2 zmienia FirstName z "Bob" na "Robert" i zapisuje zmiany
User1 zmienia FirstName na
"James”i próbuje aktualizować bazę
Wykrywanie konfliktów
// Visual C#: How the Data Adapter Configuration Wizard Supports Optimistic Concurrency
this.cmUpdate.CommandText =
"UPDATE Customers " +
"SET CustomerID=@CustomerID, CompanyName=@CompanyName " +
" WHERE (CustomerID =
@Original_CustomerID
) " +
" AND (CompanyName =
@Original_CompanyName
); " +
"SELECT CustomerID, CompanyName FROM Customers " +
" WHERE (CustomerID =
@CustomerID
)";
this.cmUpdate.Parameters.Add(new SqlParameter(
"@CustomerID
", SqlDbType.NChar, 5, ParameterDirection.Input, false, 0, 0, "CustomerID",
DataRowVersion.
Current
, null));
this.cmUpdate.Parameters.Add(new SqlParameter(
"@CompanyName
", SqlDbType.NVarChar, 40,
ParameterDirection.Input, false, 0, 0, "CompanyName", DataRowVersion.
Current
, null));
this.cmUpdate.Parameters.Add(new SqlParameter(
"@Original_CustomerID
", SqlDbType.NChar, 5,
ParameterDirection.Input, false, 0, 0 , "CustomerID", DataRowVersion.
Original
, null));
this.cmUpdate.Parameters.Add(new SqlParameter(
"@Original_CompanyName
",
SqlDbType.NVarChar, 40, ParameterDirection.Input, false, 0, 0, "CompanyName",
DataRowVersion.
Original
, null));
this.cmUpdate.Parameters.Add(new SqlParameter(
"@CustomerID
",
SqlDbType.NChar, 5, ParameterDirection.Input, false, 0, 0, "CustomerID", DataRowVersion.
Current
,
null));
Rozwiązywanie konfliktów
• Właściwość
HasErrors
– Sprawdza DataSet, DataTable, DataRow
• Jedna ze strategii:
– “Last in wins” – zmiany są zapisywane niezależnie od stanu
– Zatrzymanie wartości w DataSet i aktualizacja później
– Odrzut konfliktów i wypełnienie ich danymi z DataSet
– Odrzut konfliktów i wypełnienie ich danymi z bazy
Kontrolki źródła danych
ObjectDataSource
Umożliwia połączenie z obiektami logiki biznesowej i innych klas i służy do tworzenia aplikacji
webowych które bazują na obiektach warstwy pośredniej do zarządzania danymi. Wspiera
zaawansowane sortowania i dzielenie na strony niedostępne w innych kontrolkach źródeł
danych.
SqlDataSource
Umożliwia połączenie do serwerów baz danych takich jak Microsoft SQL Server czy Oracle.
We współpracy z serwerem SQL Server wspiera zaawansowane możliwości buforowania.
Kontrolka wspiera również sortowanie, filtrowanie i dzielenie na strony, jeśli dane są zwracane
jako obiekt DataSet.
AccessDataSource
Umożliwia współpracę z bazami danych zapisanymi w Microsoft Access. Kontrolka wspiera
sortowanie, filtrowanie i dzielenie na strony, jeśli dane są zwracane jako obiekt DataSet
XmlDataSource
Umożliwia pobieranie danych zapisanych w plikach XML, szczególnie dla hierarchicznych
kontrolek serwerowych ASP.NET takich jak TreeView. Wspiera filtrowanie przy użyciu wyrażeo
XPath i umożliwia stosowanie transformacji danych przy użyciu XSLT. Umożliwia również
aktualizację danych przez zapisanie całego dokumentu XML ze zmianami.
SiteMapDataSource Używana w ASP.NET do nawigacji na stronie
LinqDataSource
Umożliwia użycie języka LINQ na stronach ASP.NET poprzez model deklaratywny do pobrania
i modyfikowania danych z obiektów danych takich jak tabele w bazie czy kolekcje w pamięci
serwera. Wspiera automatyczne generowanie poleceń wybierania, aktualizacji, dodawania i
usuwania danych. Kontrolka wspiera sortowanie, filtrowanie i dzielenie na strony
EntityDataSource
Umożliwia połączenie z danymi pochodzącymi z modelu Entity Data Model (EDM). Wspiera
automatyczne generowanie poleceń wybierania, aktualizacji, dodawania i usuwania danych.
Kontrolka wspiera sortowanie, filtrowanie i dzielenie na strony.
Połączenie i tworzenie zapytao przy pomocy SqlDataSource
Określić atrybuty kontrolki SqlDataSource:
• ConnectionString – łańcuch połączenia.
• ProviderName – nazwa dostawcy danych.
• SelectCommand – polecenie SQL zawierające treść zapytania do bazy danych lub
nazwa procedury składowanej do wykonania
.
<asp:SqlDataSource
ID="SqlDataSource1" runat="server"
ConnectionString
="Data Source=.\SQLEXPRESS; AttachDbFilename=|DataDirectory|\CDDB.mdf;
Integrated Security=True; User Instance=True"
ProviderName="System.Data.SqlClient"
SelectCommand="SELECT * FROM [Kategorie]">
</asp:SqlDataSource>
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString
="<%$ ConnectionStrings:
PolaczenieDoBazy
%>"
SelectCommand="SELECT * FROM [Kategorie]">
</asp:SqlDataSource>
<connectionStrings>
<add name="
PolaczenieDoBazy
" connectionString="Data Source=.\SQLEXPRESS;
AttachDbFilename=|DataDirectory|\CDDB.mdf;Integrated Security=True; User Instance=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
W bloku <configuration> pliku Web.config
lub
Połączenie i tworzenie zapytao przy pomocy SqlDataSource
• Konfiguracja treści zapytania do bazy
<asp:SqlDataSource
ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:PolaczenieDoBazy %>"
SelectCommand="SELECT * FROM [Kategorie]"
DeleteCommand="DELETE FROM [Kategorie] WHERE [KatID] = @KatID"
InsertCommand="INSERT INTO [Kategorie] ([Nazwa]) VALUES (@Nazwa)"
UpdateCommand="UPDATE [Kategorie] SET [Nazwa] = @Nazwa WHERE [KatID] = @KatID">
<DeleteParameters>
<asp:Parameter Name="KatID" Type="Int32" />
</DeleteParameters>
<UpdateParameters>
<asp:Parameter Name="Nazwa" Type="String" />
<asp:Parameter Name="KatID" Type="Int32" />
</UpdateParameters>
<InsertParameters>
<asp:Parameter Name="Nazwa" Type="String" />
</InsertParameters>
</asp:SqlDataSource>
XML i Dataset
XML File
DataSet
Object
XmlDataDocument
Object
XslTransform
Object
XML or
HTML File
Database
DataAdapter
ReadXML
Doc.Save
WriteXML
XSLT File
XML File
XML File
Dlaczego używad XML z Dataset
• XML to uniwersalny format wymiany danych między klientami
• Synchronizacja i transformacja danych
Human Readable
Mainframe Readable
XML File
Or Stream
Browser Readable
Web Server
DataSet
Firewall
• Silna integracja
– serializacjaDataSet do XML
– XML jako źródło danych dla DataSet
– Schema dla DataSets zdefiniowana jako XML
schemas
– Typowane DataSet generowane z XML schema
– Dostęp do DataSet poprzez interfejs XML-DOM
• Integracja w systemach rozproszon
Metody klasy DataSet do obsługi danych i schematów XML
• ReadXML
– Ładowanie XML
• WriteXml
- zapis DataSet do strumienia XML
• WriteXmlSchema
– wygenerowanie schemy z DataSet
• ReadXmlSchema –
załadowanie XML Schema
• InferXmlSchema
– stworzenie schemy DataSet na podstawie
XML
• GetXm
l i
GetXmlSchema
– zwraca ciąg XML lub XML Schema
Metody klasy DataSet do obsługi danych i
schematów XML
• Use ReadXml to load data from a file or stream
• Use WriteXml to write XML data to a file or stream
• Use GetXml to write data to a string variable
DataSet ds = new DataSet();
ds.
ReadXml
(Server.MapPath("filename.xml"));
DataSet ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter("select * from
Authors", conn);
da.Fill(ds);
ds.
WriteXml
(Server.MapPath("filename.xml"));
string strXmlDS = ds.
GetXml
();
Synchronizacja DataSet z XmlDataDocument
• Załadowanie XML Data do XmlDataDocument
XmlDataDocument objXmlDataDoc = new XmlDataDocument();
objXmlDataDoc.
Load
(Server.MapPath ("file.xml"));
DataSet
ds
= new DataSet();
//fill in ds
objXmlDataDoc = new XmlDataDocument(
ds
);
• Załadowanie DataSet do XmlDataDocument
Praca z XmlDataDocument
• Załadowanie danych
• Pobranie wierszy jako XML
• Metody XML DOM
– XmlDataDocument dziedziczy z XmlDocument
XmlElement elem;
elem = objXmlDataDoc.GetElementFromRow(ds.Tables[0].Rows[1]);
dg.DataSource = objXmlDataDoc.DataSet;