Platformy Technologiczne

Download Report

Transcript Platformy Technologiczne

Warstwa dostępu do danych
• 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.
XML
• Integracja z XML
• Transakcje
• LINQ, Entity Framework
ADO
• ADO.NET Data Services
OLE DB
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
Mainframe
Directory
RDBMS
Email i
wiadomości
System plików
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 utrzymywać oddzielne połączenia dla każego klienta
Możliwe niepotrzebne zużycie
zasobów
idle
busy
• Serwery bazodanowe zapewniają
dostęp do kursora
przechowującego stan aktualnego
wiersza
– Dostęp do danych
– Przesuwanie się przez MoveNext
oraz MovePrevious
Klient
idle
Klienci
Połączenia
rs
Serwer
Serwer
Tabele
Kursor
Wyniki zapytania
Tryb połączeniowy
Zalety
• 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
Wady
• 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
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 zapisać zmiany do bazy
Zasoby są używane tylko gdy są
potrzebne
idle
Serwer
busy
• Dane są dostarczane do klienta w
jednej operacji
• Wyniki zapytania przechowywane w
pamięci klienta
Klient
• Zasoby serwera są zwalniane
• Klient zarządza danymi w trybie offline
• Ponowne połączenie z bazą by
zapisać zmiany
Połączenia
idle
Klienci
Serwer
rs
Kursor
Wyniki zapytania
Tabele
Tryb bezpołączeniowy
Zalety
• 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
Wady
• 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
Architektura ADO.NET
.NET Data Provider
Connection
DataAdapter
Transaction
SelectCommand
Command
Parameters
DataSet
Tables
DataTable
Fill
InsertCommand
UpdateCommand
DataRowCollection
DataColumnCollection
Update
ConstraintCollection
DataReader
DeleteCommand
DataRelationCollection
Database
ReadXml
WriteXml
XML
.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
DataSet
Connection
DataTable
Command
DataColumn
Parameter
.NET Data
Provider
DataRow
DataReader
Transaction
Constraint
DataAdapter
Klasa
DataRelation
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)
ADO.NET Class hierarchy
IDbConnection
• Interfejsy
IDbCommand
IDbConnection
IDbCommand
IDbTransaction
IDataReader
IDbTransaction
IDataReader
• Abstrakcyjne klasy bazowe
DbConnection
DbConnection
DbCommand
DbTransaction
DbDataReader
DbCommand
DbTransaction
DbDataReader
• Implementacja specjalizowana
OleDb: implementacja dla OLEDB
Sql:
implementacja dla SQL
Server
Oracle: implementacja dla Oracle
Odbc: implementacja dla ODBC
...
OleDbConnection
SqlConnection
OleDbCommand
OleDbTransaction
OleDbDataReader
OracleConnection
SqlCommand
SqlTransaction
SqlDataReader
OracleCommand
OracleTransaction
OracleDataReader
Tworzenie połączenia
• Łańcuch połączenia (ang. connection string) - ciąg znaków zawierających
parametry konfiguracji połączenia
• SqlConnection
string strConn = "data source=localhost; " +
"initial catalog=northwind; integrated security=true";
SqlConnection conn = new SqlConnection(strConn);
• Parametry ConnectionString
– Connection timeout: dopuszczalny czas
uzyskania połączenia
– Data source: nazwa instancji SQL Server lub
nazwa komputera
– User ID: konto logowania SQL Server
– Password:
–…
– Initial catalog: nazwa bazy danych
– Integrated security; gdy True połączenie z SQL
serwerem na podstawie tożsamości konta procesu
ASP.NET
http://www.connectionstrings.com/
Połączenie: Tworzenie połączenia
public interface IDbConnection
{
string ConnectionString {get; set;}
int
ConnectionTimeout {get;}
string Database
{get;}
ConnectionState State
{get;}
IDbTransaction
IDbTransaction
void
void
IDbCommand
void
BeginTransaction();
BeginTransaction(IsolationLevel il);
ChangeDatabase(string db);
Close();
CreateCommand();
Open();
BeginTransaction –
Rozpoczyna tranzakcję
ChangeDatabase – Zmiana
bazy dla otrawtego połącznia
}
ConnectionTimeout – Określenie czasu timeoutu
połączenia
Database – Nazwa bazy dla połączenia
Open, Close – Otwieranie i zamykanie
połączenia
State – Zwraca stan aktualnego połączenia: Broken,
Closed, Connecting, Executing, Fetching, or Open.
CreateCommand – Tworzy obiekt Command
powiązany z połączeniem
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łączeń
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łączeń
• 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 obiektemDataSet a
serwerem
• Obiekty DataSet są niezależne od providera
• Obiekty DataSet są przechowywane oraz przesyłane przez XML
Obiekt Command
public interface IDbCommand
{
string
CommandText
int
CommandTimeout
CommandType
CommandType
IDbConnection
Connection
IDbTransaction Transaction
UpdateRowSource UpdatedRowSource
IDataParameterCollection Parameters
{get; set;}
{get; set;}
{get; set;}
{get; set;}
{get; set;}
{get; set;}
{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
• Parameters
– Parametry, z którymi zostanie
wykonane zapytanie
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ść
– 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 when query is finished
//---- 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);
...
}
}
Zapytania parametryzowane
• Command pozwala na definiowanie
parametrów wejściowych i wyjściowych
IDataParameterCollection Parameters {get;}
• Parameter - pola
– Name: nazwa parametru
– Value: wartość parametru
– DbDataType: typ danych
– Direction: kierunek parametru
- Input
<<interface>>
IDbCommand
...
IDataParameterCollection
Parameters {get;}
...
Parameters
*
<<interface>>
IDataParameter
//----- Properties
DbType DbType {get; set;}
ParameterDirection Direction {get; set;}
string ParamterName {get; set;}
object Value {get; set;}
...
- Output
- InputOutput
- ReturnValue
<<interface>>
IDbDataParameter
//----- Properties
int Size {get; set;}
...
Zapytania parametryzowane - przykład
1. Zdefiniowanie zapytania
SQL Server: Identyfikacja parametru przez „@”(przykład: "@name")
SqlCommand cmd = new SqlCommand();
cmd.CommandText = "DELETE FROM Empls WHERE EmployeeID = @ID";
2. Dodanie parametru
cmd.Parameters.Add( new OleDbParameter("@ID", OleDbType.BigInt));
3. Przypisanie wartości
cmd.Parameters["@ID"].Value = 1234;
cmd.ExecuteNonQuery();
Tryb połączeniowy: DataReader
DataReader – Odczyt strumienia danych zwróconych przez zapytanie
• 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.
2.
3.
4.
5.
6.
Stwórz i otwórz połączenie
Stwórz obiekt Command
Stwórz DataReader dla obiektu Command
Odczytaj dane
Zamknij DataReader
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();
…
}
NextResult - Przejście do kolejnego zestawu wyników
Read – Przechodzi do następnego rekordu
Praca z IDataReader - przykład
• Stwórz obiekt i rozpocznij odczyt
IDataReader reader = cmd.ExecuteReader();
while (reader.Read()) {
• Przeczytaj kolumny do tablicy
object[ ] dataRow = new object[reader.FieldCount];
int cols = reader.GetValues(dataRow);
• Odczyt za pomocą indekserów
object val0 = reader[0];
object nameVal = reader["LastName"];
• Odczyt za pomocą metod
string firstName = reader.getString(2);
• Zamknięcie obiektu
}
reader.Close();
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ść
while
{
str
str
str
}
(myReader.Read())
+= myReader[1];
+= myReader["field"];
+= myReader.GetDateTime(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())
{
• Zamknięcie DataReader
• Zamkniecie połączenia
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
...
IDbTransaction Transaction {get; set;}
...
Transaction 1
<<interface>>
IDbTransaction
//----- Properties
IDbConnection Connection {get;}
IsolationLevel IsolationLevel {get;}
// Methods
void Commit();
void Rollback();
...
Connection 1
<<interface>>
IDbConnection
…
IDbTransaction BeginTransaction();
IDbTransaction BeginTransaction
(IsolationLevel lvl);
1. Definicja
Transakcje - przykład
SqlConnection con = new SqlConnection(connStr);
IDbTranaction trans = null;
try {
con.Open();
trans = con.BeginTransaction(IsolationLevel.ReadCommitted);
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();
3. Zatwierdzenie lub cofnięcie wyników
trans.Commit();
catch (Exception e) {
if (trans != null)
trans.Rollback();
}
finally {
try {
con.Close();
}
}
Poziomy izolacji
•
•
Definiują blokady na odczyt i zapis
ADO.NET zapewnia różne poziomy izolacji
public enum IsolationLevel {
ReadUncommitted, ReadCommitted, RepeatableRead, Serializable, ...
}
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
2.3.3 Tryb bezpołączeniowy: use DataSet
1
1.
Klient tworzy żądanie wyświetlenia strony
2.
2
Tworzenie obiektów SqlConnection i SqlDataAdapter
3.
3
Wypełnienie DataSet z DataAdapter i
4.
4
zamknięcie połączenia
Zwraca DataSet do klienta
5.
5
Zmiana danych przez użytkownika
6.
6
Aktualizacja danych DataSet
7.
7
Użycie SqlDataAdapter do otwarcia
połączenia SqlConnection, aktualizacja
danych w bazie i zamknięcie połączenia
Web server
Database
SqlConnection
SqlDataAdapter
DataSet
List-Bound
Control
Client
Czym jest DataSet?
DataSet – baza danych utrzymywana w pamięci(struktuwa relacyjna)
Tables
DataTable
DataView
DataRow(s)
Relations
DataRelation
DataColumn
Constraint(s)
DataViewManager
• DataSet zawiera wiele
DataTables
• Relacje są reprezentowane
poprzez DataRelation
• Można wymusić Constrainst
• Dostęp do danych w tabelach
poprzez DataRow i
DataColumn
DataRelation
DataTable
DataTable
DataColumn
DataRow
DataTable
DataRelation
Stosowanie obiektu DataSet
• gdy dane muszą być edytowane lub gdy do bazy trzeba dodawać
i usuwać 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 zostać 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 być 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
DataRelation
DataSet
//----- Properties
string DataSetName {get; set;}
DataRelationsCollection Relations {get;}
DataTableCollection
{get;}
Transaction Tables
1
PropertyCollection ExtendedProperties {get;}
string NameSpace {get;}
bool HasErrors {get;}
Relations
*
ParentRelations
*
ChildRelations
*
...
//----- Properties
string RelationName {get; set;}
DataTable ParentTable {get;}
DataColumn[] ParentColumns {get;}
DataTable ChildTable {get;}
DataColumn[] ChildColumns {get;}
...
DataColumn
* Tables
Columns
*
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;}
...
//----- 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;}
...
DataRow
//----- Properties
string DataSetName {get; set;}
DataRelationsCollection Relations {get;}
DataTableCollection Tables {get;}
* PropertyCollection ExtendedProperties {get;}
Rows string NameSpace {get;}
bool HasErrors {get;}
...
*
ChildColumns
*
ParentColumns
Klasa DataSet
• DataSet składa się z
– Kolecji DataTable
Dostęp do:
• DataTable
– Kolekcji DataRelation
– DataSet.Tables[0]
– DataSet.Tables[“tablename”]
• 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
•
DataColumn
– DataTable.Columns[0]
– DataTable.Columns[“columnname”]
•
DataRow
– DataTable.Rows[0]
•
Pola tabeli
– DataRow[0]
– DataRow[“columnname”]
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ść automatyczniej generacji typowanych datasetów
Może zostać zbudowany w oparciu o dane z pliku XSD
Mniej podatne na błędy
Mniejsza elastyczność
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 stworzyć 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ść
• Brak ograniczeń
• Narzut na czas implementacji
CommandBuilder
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
DataTable dt= ds.Tables["movies"];
// Use command builder to generate update commands
SqlCommandBuilder sb = new SqlCommandBuilder(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());
Data Adapter - tworzenie
• Zapisanie zapytania w DataAdapter
SqlDataAdapter da = new SqlDataAdapter
("select * from Authors",conn);
• Konstruktor ustawia wartość SelectCommand
da.SelectCommand.CommandText;
da.SelectCommand.Connection;
• Gdy wymagane, utworzenie InsertCommand, UpdateCommand,
DeleteCommand
DataSet - tworzenie
• Ładowanie danych poprzez SelectCommand obiektu DataAdapter
– Definicja SQL, przez który zostaną załadowane dane
– SelectCommand jako konstruktor
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
}
DataSet - tworzenie
• Tworzenie i załadowanie danymi DataTable
– Fill wywołuje SelectCommand obiektu DataAdapter
DataSet ds = new DataSet();
da.Fill(ds, "Authors");
• Dostęp do DataTable
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:
–
–
–
–
Przechowywać dane w wielu powiązanych tabelach
Modelować zależności między tabelami
Zarządza constrainami
Daje dostęp do widoków celem bardziej efektywnego
wyświetlania danych
– Być przesyłany pomiędzy procesami i warstwami
• DataSet i XML:
– XML może zostać załadowany do DataSet
– DataSet może zostać przesłany jako XML
– DataSet może wczytywać xsd
DataView
• DataView służy modyfikowaniu DataTable celem wyświetlenia potrzebych
danych
• DefaultView zwraca standardowy widok dla DataTable
DataView dv = ds.Tables["Authors"].DefaultView;
• Modyfikacja widoku z DataSet poprzez filtry
DataView dv = new DataView(ds.Tables["Authors"]);
dv.RowFilter = "state = 'CA'";
Trzy parametry opcjonalne
• Filter, "City='London'"
• Sort, "CompanyName ASC"
• DataViewRowState
Relacje
•
Kolumna rodzica
DataColumn parentCol = ds.Tables["Customers"].Columns["CustomerID"]
•
Kolumna dziecka
childCol = ds.Tables["Orders"].Columns["CustomerID"]
•
Stworzenie relacji
dr = New DataRelation _
(„CustOrders", parentCol, _
childCol)
ds.DataRelations.Add(dr)
parentCol
Customers table
DataSet
DataRelation definiuje relację na potrzeby nawigacji
DataRelation
childCol
Orders table
Nawigacja poprzez relacje
ds.Tables[index].Rows[index].GetChildRows("relation");
ds.Tables[index].Rows[index].GetParentRow("relation");
Orders
Customers
GetChildRows
GetParentRow
DataSet
DataView tableView;
DataRowView currentRowView;
tableView = new DataView(ds.Tables["Customers"]);
currentRowView = tableView[dgCustomers.SelectedIndex];
dgChild.DataSource = currentRowView.CreateChildView("CustOrders");
Customers
DataView
DataRowView
CreateChildView
DataSet
Orders
Modyfikacja danych w DataTable
• BeginEdit rozpoczyna edycję danych
• EndEdit i CancelEdit kończą edycję danych
DataRos drEmployee =
dtEmployees.Rows(3)
drEmployee.BeginEdit()
drEmployee("FirstName") = "John"
drEmployee("LastName") = "Smith"
Wstawianie wiersza
• Stworzenie wiersza
drEmployee.EndEdit()
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"})
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 przechowywać wiele wersji wiersza:
– DataRowVersion.Current
• Aktualna wartość
– DataRowVersion.Original
• Wartość przed dokonaniem zmian
– DataRowVersion.Proposed
• Wartość w trakcie cyklu BeginEdit / EndEdit
– DataRowVersion.Default
• Wartość 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
}
row =table.NewRow
Detached
table.Row.
Add(row)
row.
Delete
table.Rows.
Remove(row)
row[..] = ..
RejectChanges
Added
AcceptChanges
RejectChanges
Unchanged
AcceptChanges
Modified
AcceptChanges
RejectChanges
Deleted
row.Delete
row.Delete
Modyfikacja źródła danych
• Modyfikacja źródła danych przez DataAdapter
– InsertCommand, UpdateCommand, DeleteCommand
• Modyfikacje są zapisywane poprzez metodę Update obiektu DataAdapter
– DataAdapter przeszukuje wiersze pod kątem RowState
– Wykonuje akcję zgodnie ze stanem wiersza
DataRows in DataTable
RowState = Modified
Use UPDATE command
RowState = Unchanged
Ignore
RowState = Added
Use INSERT command
RowState = Modified
Use UPDATE command
RowState = Deleted
Use DELETE command
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ądzać 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
User1 czyta wiersz
Column name
Original value
Current value
Value in database
CustID
101
101
101
LastName
Smith
Smith
Smith
FirstName
Bob
Bob
Bob
User2 zmienia FirstName z "Bob" na "Robert" i zapisuje zmiany
Column name
Original value
Current value
Value in database
CustID
101
101
101
LastName
Smith
Smith
Smith
FirstName
Bob
Robert
Bob
User1 zmienia FirstName na "James”i próbuje aktualizować bazę
Column name
Original value
Current value
Value in database
CustID
101
101
101
LastName
Smith
Smith
Smith
FirstName
Bob
James
Robert
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 zapytań 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>
lub
<asp:SqlDataSource ID="SqlDataSource1" runat="server"
ConnectionString="<%$ ConnectionStrings:PolaczenieDoBazy %>"
SelectCommand="SELECT * FROM [Kategorie]">
W bloku <configuration> pliku Web.config
</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>
Połączenie i tworzenie zapytań 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
WriteXML
XML File
DataAdapter
XML File
Doc.Save
Database
DataSet
Object
XmlDataDocument
Object
XslTransform
Object
ReadXML
XML File
XML or
HTML File
XSLT File
Dlaczego używać XML z Dataset
• XML to uniwersalny format wymiany danych między klientami
• Synchronizacja i transformacja danych
Firewall
Human Readable
Web Server
XML File
Browser Readable
Or Stream
DataSet
Mainframe Readable
• 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
• GetXml 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
DataSet ds = new DataSet();
ds.ReadXml(Server.MapPath("filename.xml"));
• Use WriteXml to write XML data to a file or stream
DataSet ds = new DataSet();
SqlDataAdapter da = new SqlDataAdapter("select * from
Authors", conn);
da.Fill(ds);
ds.WriteXml(Server.MapPath("filename.xml"));
• Use GetXml to write data to a string variable
string strXmlDS = ds.GetXml();
Synchronizacja DataSet z XmlDataDocument
• Załadowanie XML Data do XmlDataDocument
XmlDataDocument objXmlDataDoc = new XmlDataDocument();
objXmlDataDoc.Load(Server.MapPath ("file.xml"));
• Załadowanie DataSet do XmlDataDocument
DataSet ds = new DataSet();
//fill in ds
objXmlDataDoc = new XmlDataDocument(ds);
Praca z XmlDataDocument
• Załadowanie danych
dg.DataSource = objXmlDataDoc.DataSet;
• Pobranie wierszy jako XML
XmlElement elem;
elem = objXmlDataDoc.GetElementFromRow(ds.Tables[0].Rows[1]);
• Metody XML DOM
– XmlDataDocument dziedziczy z XmlDocument