09/10/2015
ADO.NET
ed Entity Framework
Vittorio Maniezzo - Università di Bologna
Esempietto: anagrafica ordini
Due tabelle: clienti e ordini:
id
1
2
3
4
Clienti
nome
pippo
pluto
topolino
minnie
CREATE TABLE clienti
(id integer primary
key autoincrement,
nome text)
Vittorio Maniezzo - University of Bologna
-
id
1
2
3
4
5
6
7
8
9
10
11
12
13
idcliente
1
1
1
2
2
2
3
3
4
4
4
4
4
Ordini
codice
10
11
12
20
21
22
30
31
40
41
42
43
44
descr
dieci
undici
dodici
venti
ventuno
ventidue
trenta
trentuno
quaranta
quarantuno
quarantadue
quarantatre
quarantaquattro
CREATE TABLE ordini (id integer primary key autoincrement,
idcliente integer, codice integer, descr text, foreign
key(idcliente) references clienti(id))
2
1
09/10/2015
ADO.NET
Prima, ma ancora adesso
ODBC (Open Database Connectivity)
• Assicura interoperabilità con molti DBMS
• API molto comuni
• Usa SQL come linguaggio di accesso ai dati
OLE DB
• Ampio accesso a dati, relazionali e altro
• Interfaccia di basso livello (C++), sviluppato come COM
• Non limitato a SQL per leggere dati
• Può usare driver ODBC
ADO (ActiveX Data Objects)
• Interfaccia semplice component-based, object-oriented
• Fornisce una modalità di programmazione alle OLE DB
utilizzabile al di fuori del C++
Vittorio Maniezzo - University of Bologna
ADO
Applicazione utente
ADO
OLE DB
ODBC
OLE DB
Provider
OLE DB
Provider
Text
File Mainframe
Database
Simple Provider
Native Provider
ODBC Driver
Database
ODBC Provider
Vittorio Maniezzo - University of Bologna
2
09/10/2015
ADO.NET
ADO .NET è un’evoluzione di ADO.
ADO.NET è una collezione di classi, interfacce,
strutture, e tipi che gestisce l’accesso ai dati all’interno
del .NET Framework
• Queste collezioni sono organizzate in namespace:
System.Data,
System.Data.OleDb,
System.Data.SqlClient, etc.
Vittorio Maniezzo - University of Bologna
ADO.NET
Caratteristiche
• Scalabile, grazie a un modello progettuale non
connesso
• supporto XML (sia gerarchico che relazionale)
• Accesso a dati su HTTP
• Mantiene il modello di programmazione di ADO
Vittorio Maniezzo - University of Bologna
3
09/10/2015
ADO.NET
Managed Providers
Applicazione utente
ADO.NET Managed Provider
OLE DB
Provider
SQL Server
Database
SQL Managed Provider
Database
ADO Managed Provider
Vittorio Maniezzo - University of Bologna
Managed Provider
Un data provider .net è un insieme di classi che servono
a connettersi a una sorgente dati per ottenerne e
modificarne i dati.
DataSet
SQL Server .NET
Data Provider
SQL Server
OLE DB .NET
Data Provider
OLEDB source
Vittorio Maniezzo - University of Bologna
4
09/10/2015
ADO.NET
Accesso ai dati
Connesso: Forward-only, read-only
• L’applicazione invia query, poi legge i risultati e li
elabora
• Oggetto DataReader
Disconnesso
• L’applicazione invia query, poi memorizza i risultati
per una successiva elaborazione
• Minimizza il tempo di connessione col database
• Oggetto DataSet
Vittorio Maniezzo - University of Bologna
Architettura
Strutture dati personali
DataSet
DataReader
Managed
Data
Provider
Command
Command
Connection
Data
Store
Connection
DBMS
Vittorio Maniezzo - University of Bologna
5
09/10/2015
Oggetti di un data provider
Oggetto
Descrizione
Connection
Stabilisce una connessione con una specifica
sorgente dati.
Command
Esegue un comando sulla sorgent dati. Espone
dei Parameters e accetta Transaction da una
Connection.
DataReader
Legge uno stream di dati forward-only, read-only
da una sorgente dati.
DataAdapter
Riempie un DataSet e gestisce gli aggiornamenti
con la sorgente dati.
Vittorio Maniezzo - University of Bologna
Architettura
Vittorio Maniezzo - University of Bologna
6
09/10/2015
Classi ADO.NET
Namespace System.Data
Contiene le classi di base di ADO.NET
Fondamentale è DataSet (disconnesso)
Supporta tutti i tipi di applicazioni
• Internet
 ASP.NET
 XML
• Windows forms
Contiene classi usate o derivate da managed providers
IDbConnection, IDbCommand, IDbDataReader
Vittorio Maniezzo - University of Bologna
Classi ADO.NET
Namespace System.Data.SqlClient
Managed provider nativo per SQL Server
Classi SqlConnection, SqlCommand e
SqlDataReader
Classi per
• Error handling
• Connection pooling
System.Data.SqlTypes contiene le classi
per i tipi di dati SQL Server nativi
Vittorio Maniezzo - University of Bologna
7
09/10/2015
Classi ADO.NET
Namespace System.Data.SQLite
Managed provider nativo per sqlite
Classi SQLiteConnection,
SQLiteCommand e SQLiteDataReader
Classi per
• Error handling
• Connection pooling
System.Data.SQLiteTypes contiene le
classi per i tipi di dati SQL Server nativi
Vittorio Maniezzo - University of Bologna
Classi ADO.NET
Interfaccia IDbConnection
Permette di astrarre la specifica tecnologia che si utilizza.
Crea una sessione unica con una sorgente dati
Implementata da SqlDbConnection,
SQLiteConnection, OleDbConnection, …
Funzionalità
• Open, close di connessioni
• Inizio di transazioni
IDbTransaction fornisce metodi Commit e
Rollback (v. dopo)
Vittorio Maniezzo - University of Bologna
8
09/10/2015
Classi ADO.NET
Interfaccia IDbCommand
Rappresenta una istruzione da inviare alla sorgente dati di solito,
ma non necessariamente, SQL
Implementata da SQLiteCommand, SqlCommand, ...
Funzionalità
• Definisce l’istruzione da eseguire
• Esegue l’istruzione
• Passa e riceve parametri
ExecuteReader ritorna le righe,
ExecuteNonQuery non ritorna niente,
ExecuteScalar ritorna un singolo valore, il primo della
prima riga della view
Vittorio Maniezzo - University of Bologna
Classi ADO.NET
Interfaccia IDbTransaction
Esempio di due comandi (insert e select) in un’unica
transazione
using (IDbConnection con = new SqlConnection(conString))
{ try
{ con.Open();
using (var transaction = con.BeginTransaction() )
{ command = new SqlCommand("insert into ... ,con);
command.Transaction = transaction;
// primo comando
nrows = command.ExecuteNonQuery();
command.CommandText = "select ...
command.Transaction = transaction;
// secondo comando
IDStorico=(int) command.ExecuteScalar();
transaction.Commit();
// o entrambi i comandi o nessuno
}
con.Close();
}
catch(Exception ex) { ...
Vittorio Maniezzo - University of Bologna
18
9
09/10/2015
Classi ADO.NET
Interfaccia IDataReader
Accesso forward-only, read-only a stream di dati
Implementata da SqlDataReader e
SQLiteDataReader, ...
Creata col metodo ExecuteReader di IDbCommand
Le operazioni sull’oggetto associato IDbConnection
sono bloccate finchè il reader non viene chiuso
Vittorio Maniezzo - University of Bologna
ADO.NET, utilizzo connesso
IDbDataReader
IDbCommand
IDbConnection
DBMS
In un contesto connesso, le
risorse sono mantenute sul
server finchè non viene
chusa la connessione
Operazioni
1. Apri la connessione
2. Esegui un comando
3. Elabora le righe nel reader
4. Chiudi il reader
5. Chiudi la connessione
Vittorio Maniezzo - University of Bologna
10
09/10/2015
Classi ADO.NET
Esempio DataReader (SqLite)
IDbConnection conn = new SQLiteConnection(sqLiteConnString);
conn.Open();
IDbCommand com = conn.CreateCommand();
string queryText = "select id,nome from clienti";
com.CommandText = queryText;
IDataReader reader = com.ExecuteReader();
while (reader.Read())
{
view.textConsole = reader["id"]+" "+reader["nome"];
}
reader.Close();
conn.Close();
Vittorio Maniezzo - University of Bologna
Stringhe connessione
(v. http://www.connectionstrings.com/)
Sql Server
Server=myServerAddress;Database=myDataBase;User
Id=myUsername; Password=myPassword;
Esempio
connectionString = @"Data Source=tcp:137.204.74.181;
Initial Catalog=studenti;User ID=***;Password=***";
SqLite (NuGet: Install-Package System.Data.SQLite)
Data Source=filename;Version=3;
Esempio
connString = @"Data Source="+dbpath+"; Version=3";
Postgres
Server=127.0.0.1;Port=5432;Database=myDataBase;User
Id=myUsername;Password=myPassword;
Esempio
string MyConString = "Server=localhost;UID=studenti;
PWD=laPWD;Database=studenti;Port=5432;";
Vittorio Maniezzo - University of Bologna
11
09/10/2015
Parametrized query
I commandText sono semplici stringhe SQL, spesso costruite
dinamicamente. Es, where delle select, values delle insert, …
Pericolo SQL injection, e comunque opportuno un controllo dei
tipi. Viene fatto con query parametrizzate, tre fasi:
1. costruzione della stringa del comando con parametri.
2. dichiarazione di un oggetto Parameter, assegnandogli i valori
richiesti.
3. assegnazione dell'oggetto Parameter alla proprietà
Parameters dell'oggetto Command.
Vittorio Maniezzo - University of Bologna
23
Parametrized query: comando
Prima fase: costruzione di una stringa di comando che
contiene dei segnaposto per i parametri.
I segnaposto saranno riempiti con i valori effettivi
quando verrà eseguito il comando.
I parametri sono connotati da un '@' prefisso al nome
del parametro, es:
IDbCommand com = conn.CreateCommand();
com.CommandText = “select nome from clienti”;
com.CommandText += “ where id = @id";
Vittorio Maniezzo - University of Bologna
24
12
09/10/2015
Parametrized query: Parameter
Seconda fase: si definisce un'istanza di IDbDataParameter per
ogni parametro nel comando, es.:
IDbDataParameter param = com.CreateParameter();
param.DbType = DbType.Int32;
param.ParameterName = "@id";
param.Value = 1;
Terza fase: assegnazione di ogni parametro alla collection
Parameters del comando:
com.Parameters.Add(param);
Vittorio Maniezzo - University of Bologna
25
Parametrized query: esempio
Esempio completo utilizzo di query parametrica:
com.CommandText = "select nome from clienti where id=@id";
IDbDataParameter param = com.CreateParameter();
param.DbType = DbType.Int32;
param.ParameterName = "@id";
param.Value = 1;
com.Parameters.Add(param);
using (IDataReader dr = com.ExecuteReader())
{ while (dr.Read())
view.textConsole = "nome: "+ dr["nome"];
}
Vittorio Maniezzo - University of Bologna
26
13
09/10/2015
Indipendenza dalla tecnologia
• Le stringhe di connessione sono gestite al meglio in un file di
config, non direttamente nel codice.
• In un progetto windows form, il config è di solito nel file
App.config.
• Per inserire la gestione delle stringhe, il progetto deve
referenziare l’assembly System.Configuration
L’accesso alla stringa specifica avveine con una istruzione del tipo
ConfigurationManager.ConnectionStrings["SQLiteConn"].ConnectionString;
Nell’App.config si inserisce una sezione come la seguente
<connectionStrings>
<add name="SQLiteConn" connectionString="Data Source=PATH;Version=3;"
providerName="System.Data.SQLite" />
<add name="SqlServerConn" connectionString="Data
Source=tcp:137.204.74.181;Initial Catalog=studenti;User ID=***;Password=***"
providerName="System.Data.SqlClient" />
</connectionStrings>
Vittorio Maniezzo - University of Bologna
27
Indipendenza dalla tecnologia
Nell'esempio proposto il codice fa poco riferimento alla
tecnologia utilizzata (sqlite, sql server, …).
I riferimenti sono ridotti a una riga di codice grazie all’uso diretto
delle interfacce.
Un uso esplicito dei provider può risultare in maggiore efficienza
ma minore flessibilità.
E’ possibile astrarsi completamente dalla tecnologia, ad es. con le
DbProviderFactories contenute nel namespace
System.Data.Common, come nel seguito.
Oppure usare un ORM (v. ancora dopo)
Vittorio Maniezzo - University of Bologna
28
14
09/10/2015
DbProviderFactories
Il namespace System.Data.Common definisce un
insieme di classi astratte che sono implementate da
tutti i provider di dati.
L'intento è quello di fornire un modo agnostico di
accesso ai dati, che non faccia riferimenti diretti agli
assembly dei provider ADO.NET.
La classe DbProviderFactories carica il provider di dati
.NET richiesto e restituisce una classe concreta
conosciuta come DbProviderFactory.
DbProviderFactory funge da factory per DbConnection,
DbCommand e gli altri oggetti ADO.NET.
Vittorio Maniezzo - University of Bologna
29
DbProviderFactories
Per usare DbProviderFactories è necessario che il provider
sia noto al sistema.
Se non lo è (sqlite attualmente non lo è) bisogna inserire
queste righe nell'app.config (o nel web.config).
<system.data>
<DbProviderFactories>
<remove invariant="System.Data.SQLite"/>
<add name="SQLite Data Provider" invariant="System.Data.SQLite"
description=".Net Framework Data Provider for SQLite"
type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
</DbProviderFactories>
</system.data>
Prossimo esempio (DataSet) con DbProviderFactories (ma
le si possono usare anche col DataReader).
Vittorio Maniezzo - University of Bologna
30
15
09/10/2015
Classi ADO.NET
DataSet
• E’ una collezione di tabelle
• Non conosce la sorgente dei dati
• Gestisce le relazioni fra le tabelle
• Potente interfaccia di programmazione (ha
oggetti per tabelle, colonne, relazioni, ecc)
• Ricorda lo stato originale e corrente dei dati
• Può modificare dati e metadati
• Il formato di serializzazione nativo è XML
Vittorio Maniezzo - University of Bologna
Classi ADO.NET
DataSet
DataSet
DataTable
DataColumn
DataRow
DataRelation
Vittorio Maniezzo - University of Bologna
16
09/10/2015
Classi ADO.NET
Interfaccia IDataAdapter
• Riempie o aggiorna una tabella di un DataSet
• Implementata da IDbDataAdapter
• Contiene 4 oggetti predefiniti per operazioni sul
dataset: Select, Insert, Update e Delete
• Se solo in lettura, si può riutilizzare lo stesso
dataAdapter per leggere tabelle successive, se
serve sincronizzare tabelle del dataset, si deve
usare un dataAdapter diverso per ogni tabella.
Vittorio Maniezzo - University of Bologna
ADO.NET utilizzo disconnesso
DataSet
IDbDataAdapter
IDbConnection
DBMS
In un contesto disconnesso, le
risorse NON sono gestite sul
server quado i dati vengono
elaborati
Operazioni
1. Apri la connessione
2. Riempi il DataSet
3. Chiudi la connessione
4. Elabora il DataSet
5. Apri la connessione
6. Aggiorna la sorgente dati
7. Chiudi la connessione
Vittorio Maniezzo - University of Bologna
17
09/10/2015
Classi ADO.NET
DataTable
Oggetto in memoria che rappresenta una tabella
• Colonne
• Righe
Schema definito dalla collection Columns
Integrità dati gestita dall’oggetto Constraint
Eventi pubblici
• Modifica/cancellazione righe
• Modifica colonne
Vittorio Maniezzo - University of Bologna
Classi ADO.NET
DataColumn
Elemento fondamentale di un DataTable (contenuto
nella collection Columns)
Definisce quali tipi di dati possono entrare (per mezzo
della proprietà DataType)
Altre proprietà: AllowNull, Unique e ReadOnly
Può contenere Constraints (una collection su
DataTable)
Può contenere Relations (collection su DataSet)
Vittorio Maniezzo - University of Bologna
18
09/10/2015
Classi ADO.NET
DataRow
Rappresenta i dati in una DataTable (contenuto
nella collection Rows)
Conforme allo schema definito da DataColumns
Proprietà per determinare lo stato di una riga
(new, changed, deleted ecc.)
Vittorio Maniezzo - University of Bologna
Classi ADO.NET
DataRelation
Correla due DataTables via DataColumns
I valori di DataType delle due DataColumns
devono essere identici
Le modifiche non corrispondenti alla relazione
sono proibite
Vittorio Maniezzo - University of Bologna
19
09/10/2015
DataSet
DataRowCollection
DataSet
DataRow
DataTableCollection
DataTable
DataColumnCollection
DataColumn
DataRelationCollection
DataView
DataRelation
Vittorio Maniezzo - University of Bologna
La classe DataSet
Un DataSets consiste di una o più tabelle da:
• Uno o più data adapter.
• Creazione da programma.
• XML.
• Altri DataSets.
Le tabelle contengono colonne, vincoli, e righe.
• Il concetto di “riga corrente” non esiste.
• Tutto è una collection!
DataColumn
DataTable
DataRelation
DataRow
Constraints
Vittorio Maniezzo - University of Bologna
20
09/10/2015
Classi ADO.NET
Esempio DataSet (SqLite, con provider factory)
DataSet ds = new DataSet();
ConnectionStringSettings connString = ConfigurationManager.ConnectionStrings["SQLiteConn"];
DbProviderFactory dbFactory = DbProviderFactories.GetFactory("System.Data.SQLite");
using (DbConnection conn = dbFactory.CreateConnection())
{
try
{ conn.ConnectionString = connString.ConnectionString;
conn.Open();
DbDataAdapter dbAdapter = dbFactory.CreateDataAdapter();
DbCommand dbCommand = conn.CreateCommand();
dbCommand.CommandText = "select id,nome from clienti";
dbAdapter.SelectCommand = dbCommand;
dbAdapter.Fill(ds);
ds.Tables[0].TableName = "clienti";
foreach(DataRow dr in ds.Tables["clienti"].Rows)
view.textConsole = dr["nome"].ToString();
}
catch (Exception ex)
{ view.textConsole = "[FillDataSet] Error: " + ex.Message;
}
finally
{ if (conn.State == ConnectionState.Open) conn.Close();
}
}
Vittorio Maniezzo - University of Bologna
Membri classe DataSet
Clear
Clears the DataSet of any data by removing all rows in all
tables.
Clone
Copies the structure of the DataSet, including all DataTable
schemas, relations, and constraints. Does not copy any data.
Copy
Copies both the structure and data for this DataSet.
Merge
Merges this DataSet with a specified DataSet.
Vittorio Maniezzo - University of Bologna
21
09/10/2015
Membri classe DataSet
AcceptChanges
Commits all the changes made to this DataSet since it
was loaded or the last time AcceptChanges was called.
RejectChanges
Rolls back all the changes made to the DataSet since it
was created, or since the last time
DataSet.AcceptChanges was called.
GetChanges
Gets a copy of the DataSet containing all changes made
to it since it was last loaded, or since AcceptChanges was
called.
HasChanges
Gets a value indicating whether the DataSet has changes,
including new, deleted, or modified rows.
Vittorio Maniezzo - University of Bologna
Membri classe DataSet
GetXml
Returns the XML representation of the data stored in
the DataSet.
GetXmlSchema
Returns the XSD schema for the XML representation of
the data stored in the DataSet.
ReadXml
Reads XML schema and data into the DataSet.
ReadXmlSchema
Reads an XML schema into the DataSet.
WriteXml
Writes XML data, and optionally the schema, from the
DataSet.
WriteXmlSchema
Writes the DataSet structure as an XML schema.
I DataSet sono strutture dati in memoria, possono esistere
indipendentemente da un database. Nel seguito esempio di
creazione in memoria di una struttura.
Vittorio Maniezzo - University of Bologna
22
09/10/2015
DataSet: select, insert, update, delete
using (DbConnection conn = dbFactory.CreateConnection())
{ try
{ conn.ConnectionString = connString;
conn.Open();
Indipendenza via
dbFactory
DataAdapter
// tabella clienti.
specifico per il
DbDataAdapter daClienti = dbFactory.CreateDataAdapter();
daClienti.SelectCommand = conn.CreateCommand();
provider utilizzato
daClienti.SelectCommand.CommandText = "SELECT id, nome FROM clienti";
daClienti.TableMappings.Add("Table", "clienti");
// override del nome di default
daClienti.Fill(ds);
TableMapping, per
// tabella ordini.
comodità
DbDataAdapter daOrdini = dbFactory.CreateDataAdapter();
daOrdini.SelectCommand = conn.CreateCommand();
daOrdini.SelectCommand.CommandText = "select id, idcliente, codice, descr from ordini";
daOrdini.TableMappings.Add("Table", "ordini");
daOrdini.Fill(ds);
// filtra alcuni ordini
DataTable tabOrdini = ds.Tables["ordini"];
string filtro = "codice >= 10 AND codice <= 30";
string ordine = "id DESC";
DataRow[] foundRows = tabOrdini.Select(filtro, ordine);
Filtraggio dati
tabella, un
esempio
...
45
Vittorio Maniezzo - University of Bologna
-
DataSet: select, insert, update, delete
...
DbCommandBuilder cb = dbFactory.CreateCommandBuilder();
cb.DataAdapter = daOrdini;
cb.GetInsertCommand();
cb.GetUpdateCommand();
cb.GetDeleteCommand();
//insert command
DataRow newOrder = tabOrdini.NewRow();
newOrder["idcliente"]=2;
newOrder["codice"]=25;
newOrder["descr"]="nuovo venticinque";
tabOrdini.Rows.Add(newOrder);
//update command
DataRow[] editRow = tabOrdini.Select("codice = 25");
editRow[0]["descr"] = "venticinque cambiato";
//delete command
DataRow[] deleteRow = tabOrdini.Select("codice = 25");
foreach (DataRow row in deleteRow)
row.Delete();
//ds.AcceptChanges();
daOrdini.Update(ds);
CommandBuilder,
indispensabile
Insert, crea nuova
riga
Update, modifica
dato di riga esistente
Delete, cancella riga
esistente
Sincronizzazione
database
// aggiorna il dataset, tutte righe unchanged (non le salva)
// aggiorna il database con le modifiche al dataset
}
catch ( ...
Vittorio Maniezzo - University of Bologna
-
46
23
09/10/2015
DataSet: creazione tabella
DataSet in
memoria
DataSet ds = new DataSet("DynamicDS");
ds.Tables.Add(“Ordini");
ds.Tables["Ordini"].Columns.Add("OrderID",
Type.GetType("System.Int32"));
ds.Tables["Ordini"].Columns.Add("CustomerFirstName",
Type.GetType("System.String"));
ds.Tables["Ordini"].Columns.Add("Data",
Type.GetType("System.DateTime"));
ds.Tables.Add(“RigheOrdine”);
Vittorio Maniezzo - University of Bologna
DataSet: aggiunta dato
DataSet in
memoria
DataRow newRow;
newRow = ds.Tables["Ordini"].NewRow();
newRow["OrderID"] = 101;
newRow["CustomerFirstName"] = “Giovanni";
newRow["CustomerLastName"] = "Ducci";
newRow["Date"] = new DateTime(2009, 5, 1);
m_ds.Tables["Ordini"].Rows.Add(newRow);
Vittorio Maniezzo - University of Bologna
24
09/10/2015
Lettura DataTable
DataSet in
memoria
DataTable myTable = ds.Tables[“RigheOrdine"];
foreach (DataColumn c in myTable.Columns)
{ Console.Write(c.ColumnName + "\t");
}
Console.WriteLine("");
foreach (DataRow r in myTable.Rows)
{ foreach (DataColumn c in myTable.Columns)
Console.Write(r[c] + "\t");
Console.WriteLine("");
}
Vittorio Maniezzo - University of Bologna
Aggiunta di una colonna
DataSet in
memoria
Colonna normale:
ds.Tables["Ordini"].Columns.Add("OrderID",
Type.GetType("System.Int32"));
Colonna calcolata:
workTable.Columns.Add("Totale", typeof(Double));
workTable.Columns.Add(“Tasse",typeof(Double),"Total*0.086);
workTable.Columns.Add(“Totalone",typeof(Double),"Max(Total)");
Vittorio Maniezzo - University of Bologna
25
09/10/2015
Aggiunta di una colonna
DataSet in
memoria
Colonna Autoincrementata:
DataColumn workColumn =
workTable.Columns.Add("CustomerID",typeof(Int32));
workColumn.AutoIncrement = true;
workColumn.AutoIncrementSeed = 200;
workColumn.AutoIncrementStep = 3;
Vittorio Maniezzo - University of Bologna
Primary Key
DataSet in
memoria
workTable.PrimaryKey = new DataColumn[]
{workTable.Columns["CustLName"],
workTable.Columns["CustFName"]};
Oppure
DataColumn[] myKey = new DataColumn[2];
myKey[0] = workTable.Columns["CustLName"];
myKey[1] = workTable.Columns["CustFName"];
workTable.PrimaryKey = myKey;
Vittorio Maniezzo - University of Bologna
26
09/10/2015
DataRelation e Constraint
DataSet in
memoria
DataColumn parentCol =
m_ds.Tables["Ordini"].Columns["OrderID"];
DataColumn childCol =
m_ds.Tables[“RigheOrdine"].Columns["fk_OrderID"];
// Crea e aggiunge la relazione
m_ds.Relations.Add(new DataRelation("Ordini_RigheOrdine",
parentCol,
//primary key
childCol));
InsertOrderDetailRecord(103, "Item-200", 9, "140.77");
Vittorio Maniezzo - University of Bologna
Vincolo Unique
DataSet in
memoria
Il vincolo unique assicura che tutti i dati nella/e colonna/e
specificata/e sono unici per riga.
Una DataTable.PrimaryKey è unique.
DataColumn[] myColumns = new DataColumn[2];
myColumns[0] = myTable.Columns["id"];
myColumns[1] = myTable.Columns["Name"];
UniqueConstraint myUC = new UniqueConstraint("idNameConstrnt",
myColumns, false);
myTable.Constraints.Add(myUC);
Vittorio Maniezzo - University of Bologna
27
09/10/2015
Vincolo ForeignKey
DataSet in
memoria
Un ForeignKeyConstraint impone regole su come vengono
propagati gli update e i delete a tabelle correlate.
ForeignKeyConstraint custOrderFK = new ForeignKeyConstraint
("Order_OrderDetail",
m_ds.Tables["Ordini"].Columns["OrderID"],
m_ds.Tables[“RigheOrdine"].Columns["fk_OrderID"]));
custOrderFK.DeleteRule = Rule.None;
custDS.Tables[“RigheOrdine"].Constraints.Add(custOrderFK);
Non si può cancellare un ordine che ha
associato delle righe d’ordine.
Vittorio Maniezzo - University of Bologna
DataView
• Una DataView permette di creare viste diverse sui
dati memorizzati in una DataTable.
• Con una DataView si possono visualizzare i dati di
una tabella con ordinamenti diversi, o filtrarli in
base ai loro valori.
Vittorio Maniezzo - University of Bologna
28
09/10/2015
DataGrid e DataView
string sQueryString = "SELECT * FROM test";
SQLiteDataAdapter myDSAdapter = new SQLiteDataAdapter();
DataSet myDataSet = new DataSet();
myDSAdapter.SelectCommand = new SQLiteCommand(sQueryString, conn);
myDSAdapter.Fill(myDataSet);
DataTable Cart = new DataTable();
Cart = myDataSet.Tables[0];
DataView CartView = new DataView(Cart);
dg.DataSource = CartView;// per collegare a un dataGridView
Vittorio Maniezzo - University of Bologna
Entity Framework
2002
ADO .Net v1.0
2005
ADO .Net v2.0
• Object Oriented Datasets.
• Disconnected Model.
• Provider Abstraction.
•
•
•
•
•
•
Provider Factories
Metadata schema
Asynchronous Command Execution
Conflict Detection
Batch Updates
Multiple Active Result Sets (MARS)
Vittorio Maniezzo - University of Bologna
29
09/10/2015
Entity Framework
2006
• nessuna variazione in ADO.net
ADO .Net v3.0
2007
ADO .Net v3.5
• Paging Support
• Synchronization APIs
• Language integrated Query
(LINQ)
• LINQ to SQL
Vittorio Maniezzo - University of Bologna
Entity Framework
2008
ADO .Net v3.5
SP1
• Entity Data Model (EDM)
• Object Services
• LINQ to Entities
• Entity SQL
• Entity Client
• ADO .Net Data Services
2016
EF7.0
ASP.NET 5
• “A major release”
• Vedremo …
Vittorio Maniezzo - University of Bologna
30
09/10/2015
Object Relational Mapping (ORM)
Vittorio Maniezzo - University of Bologna
Tecnologie ORM
Object-Relational Mapping (ORM) è una tecnica di
programmazione per associare e convertire
automaticamente dei dati fra tabelle di database e
classi/oggetti OO
ORM crea una sorta di database a oggetti utilizzabile da
codici a oggetti come C# o Java
I framework ORM (object-relational persistence
frameworks) automatizzano il processo ORM.
Vittorio Maniezzo - University of Bologna
31
09/10/2015
ORM Framework
I framework ORM tipicamente forniscono le seguenti
funzionalità:
• Creazione di un object model dal database schema
• Creazione di un database schema dall'object model
• Query dei dati via object-oriented API
• Manipolazione dati CRUD: create, retrieve, update,
delete
I framework ORM generano automaticamente query SQL
per effettuare le operazioni richieste sui dati
Vittorio Maniezzo - University of Bologna
ORM Mapping – Esempio
Esempio di mapping per un sottinsieme del database
Northwind
ORM Entities
schema database
(Classi C#)
relazionale
ORM
Framework
Vittorio Maniezzo - University of Bologna
32
09/10/2015
Vantaggi ORM
• Produttività: si scrive meno codice
• Si nascondono le differenze fra la rappresentazione
relazionale e ad oggetti, la complessità è gestita
dall'ORM
• Semplificazione delle operazioni CRUD per relazioni
complesse
• Manutenzione più semplice
Vittorio Maniezzo - University of Bologna
Entity Framework e ADO.NET
• Entity Framework (EF) è un framework ORM open source,
parte di .NET
• Fornisce una infrastruttura run-time per gestire dati in
database SQL come oggetti .NET
• Lo schema del database relazionale è associato a un
modello a oggetti (object model)
• Visual Studio contiene dei tool per generare le
associazioni (Entity Framework SQL data mapping).
• I data mapping consistono di classi C# e di XML
• Esistono anche altri ORM per .net: DataObjects.Net,
NHibernate, OpenAccess, SubSonic ecc. ,
Vittorio Maniezzo - University of Bologna
33
09/10/2015
Supporto a Database diversi per
Entity Framework
Entity Framework Providers
•
•
•
•
•
•
•
•
MySQL Connector\net (MySQL)
Devart (Oracle, MySQL, SQLite and PostgreSQL)
IBM (DB2, Informix and U2 databases)
Phoenix Software Solutions (SQLite)
Npgsql (PostgreSQL)
Sybase SQL Anywhere (SQL Anywhere 11)
Firebird (Firebird)
VistaDB (VistaDB 4.x)
v. http://msdn.microsoft.com/en-us/data/dd363565.aspx
Vittorio Maniezzo - University of Bologna
Entity Framework e ADO.NET
Permette di:
• Generare classi dal modello
• Aggiornare le classi se il modello cambia
• Gestire le connessioni al database
• Effettuare le query sul modello, non sul database
• Tradurre le query al modello a query al database
• Gestire cambiamenti agli oggetti del modello
• Gestire update al database
Vittorio Maniezzo - University of Bologna
34
09/10/2015
Entity Framework: Architettura
Vittorio Maniezzo - University of Bologna
Entity Framework: Architettura
Binding
Mapping
Modeling
Browsing
Domain Modeling
Tools
Code Gen
Applications
Entity Services
Programming Layers
Reporting
Analysis
Sync
Search
Linq to Entities
Entity SQL
Entity Client – EDM, Entity SQL
Metadata
Services
Query and
Update Pipelines
Mapping
Transactions
Entity Framework
Runtime
Data Providers (ADO.NET)
SqlClient
SQL Server
OtherClient
Relational
DBMS
Nonrelational
Web Service
Vittorio Maniezzo - University of Bologna
35
09/10/2015
EF: componenti architettura
EDM (Entity Data Model): consiste di tre component:
• Conceptual Model: contiene le classi del modello e le
loro relazioni. Non dipende direttamente dal database.
• Storage Model: è il modello della struttura del databse,
incude tabelle, viste, stored procedures, oltre che
relazioni e chiavi (primarie, esterne, … ).
• Mapping: contiene le inforazioni su come gli elementi
del modello concettuale sono correlate a quelli dello
storage.
Vittorio Maniezzo - University of Bologna
71
EF: componenti architettura
Interrogazione, può avvenire con tre modalità:
• LINQ to Entities: LINQ to Entities è un linguaggio di
query che può interrogare l’object model. Ritorna delle
entità, definite nel modello concettuale.
• Entity SQL: Entity SQL è un altro linguaggio di query,
molto simile a sql, che permette di lavorare
direttamente su collezioni di entità.
• POCO, Plain Old CLR Objects (!), lavora sulle collezioni
di entità dell’object model con gli opratori c# standard.
Vittorio Maniezzo - University of Bologna
72
36
09/10/2015
EF: componenti architettura
Object Service: è l’entry point del codice utente per accedere ai
dati del database. È responsabile della materializzazione dei
dati, cioè della conversion dei dati ricevuti dal livello
sottostante in entità strutturate come da modello.
Entity Client Data Provider: converte le richieste (L2E, Entity SQL,
POCO) in query SQL da passare al database. Comunica con il
provider ADO.Net che fa da interfaccia al database.
ADO.Net Data Provider: comunica con il database usando
ADO.Net.
Vittorio Maniezzo - University of Bologna
73
VS2015 e EF6: definizione del progetto
Sia VS2015 che EF6 sono tecnologie recenti e la loro integrazione con
strumenti non MicroSoft non è ancora molto robusta.
Per accedere al database SqLite con EF6 da VisualStudio 2015 procedure
come segue.
1 – Installare la versione SqLite adeguata. Alla data odierna NON USARE il
pacchetto NuGet ma collegarsi alla pagina
http://system.data.sqlite.org/index.html/doc/trunk/www/downloads.
wiki
(assolutamente complicata) e scaricare “Setups for 32-bit Windows
(.NET Framework 4.6)”. Controllare che il progetto sia framework 4.6
2 – In VS2015, clickare col destro sul progetto (in solution explorer) e
selezionare “aggiungi / Nuovo elemento”.
3 – Dal wizard dell’Add new item, selezionare “ADO.NET Entity Data
Model”. Questo attiva il wizard dell’Entity data model.
4 – Selezionare “Entity framework designer da database”
5 – selezionare “Nuova connessione”
Vittorio Maniezzo - University of Bologna
37
09/10/2015
VS2015 e EF6: definizione del progetto
6 - selezionare il provider per SQLite (se non c’è è stato installato il provider sbagliato.
Tornare al passo 1).
7 - cliccare su browse e selezionare il file sqlite da utilizzare.
8 - selezionare gli elementi del database da includere nel modello
9 - fine, il modello viene generato automaticamente
Nota: i passi 5 – 7 possono essere saltati se si crea in anticipo la connessione al database.
Alcuni tutorial che possono essere di aiuto:
http://www.entityframeworktutorial.net/
https://erazerbrecht.wordpress.com/2015/06/11/sqlite-entityframework-6-tutorial/
https://www.devart.com/dotconnect/db2/docs/Tutorial_EF.html
Vittorio Maniezzo - University of Bologna
75
Entity Data Model (EDM)
I passi precedenti hanno portato alla creazione di
un modello (io Model1), contenuto in un file xml
Model1.emdx e visualizzato in solution explorer.
Il designer mostra la struttura del modello.
Questa vista in realtà è basata sul file xml, che
contiene le tre sezioni principali SSDL (storage
model), CSDL (conceptual model) e C-S mapping.
Anche il file app.config viene
modificato aggiornando le sezioni
opportune (configSections,
startup, connectionStrings,
entityFramework)
Vittorio Maniezzo - University of Bologna
76
38
09/10/2015
Model browser
Cliccando col destro sul diagramma del modello
concettuale si può aprire il model browser.
Il model browser mostra questi elementi:
• Diagrammi: i diagrammi visuali dell’EDM.
• Tipi di Entità: lista di tutte le classi (tipi) mappati sulle
tabelle del DB.
• Tipi complessi: Classi generate dall’ EDM come
risultato di stored procedures, funzioni che generano
tabelle, ecc.
• Tipi enum: lista di tutte le entità usate come enum
dall’ entity framework.
• Associazioni: relazioni foreign key fra entità.
• Importazioni di funzioni: funzioni che saranno
mappate sulle stored procedures, ecc. E cje quindi
saranno usate come funzioni dall’ EF.
• esempioModel.Store: lo schema del database (SSDL).
Vittorio Maniezzo - University of Bologna
77
Approcci progettuali
Tre modalità secondo cui definire il modulo EF:
1. Database First
Si ipotizza di avere già il database completo, e si generano le
entità sulla base del db: si genera l’EDMX, cioè le classi EDM, il
dbContext e le entità da un database esistente.
2. Code First
Si scrivono per prime le classi POCO che definiscono il dominio
e quindi si crea database da queste classi per rendere
persistenti i dati.
3. Model First
Si creano entità, relazioni e gerarchie di ereditarietà
direttamente sull'area di progettazione di EDMX, nel designer,
e quindi si generano le classi e il database dal modello.
Vittorio Maniezzo - University of Bologna
78
39
09/10/2015
DbContext
Creando un EDM viene generate la classe
esempioEntities che specializza la DbContext nel
modello corrente.
Creando un EDM viene generate la classe esempioEntities che specializza la
DbContext nel modello corrente.
Questa classe è il collegamento fra il codice e il DB. È responsabile di:
• EntitySet: contiene un entity set (DbSet<TEntity>) per tutte le entità che
sono mappate su tabelle del.
• Query: converte le query L2E, … , in SQL e le inoltra al database.
• Change Tracking: memorizza i cambiamenti subiti dale entità dopo
essere state lette dal DB.
• Persisting Data: Esegue Insert, Update e Delete sul DB in funzione dello
stato dele entità.
• Caching: delle entità lette.
• Gestione delle relazioni: responsabile del controllo delle relazioni.
• Object Materialization: converte i dati delle tabelle in oggetti (entità).
Vittorio Maniezzo - University of Bologna
79
DbSet
La classe DBSet rappresenta un insieme di entità che viene
utilizzato per creare, leggere, aggiornare ed eliminare dati.
Usando DBContext è possibile ottenere il riferimento di DBSet, es.
context.ordini o context.clienti
Intuitivamente, il DbContext
corrisponde al database (o a
una collezione di tabelle e viste
del database) e il DbSet corrisponde a una tabella o vista.
Si usa un oggetto DbContext per accedere alle tabelle e viste,
che saranno rappresentate da DbSet, e i DbSet per accedere,
creare, aggiornare, cancellare e modificare i dati delle tabelle.
Vittorio Maniezzo - University of Bologna
80
40
09/10/2015
Metodi del DbSet (i principali)
Metodo
Descrizione
Add(Entity)
Aggiunge una entità al context, in stato added. Viene passata sul db con
SaveChanges diventando unchanged.
context.ordini.Add(newOrder)
Attach(Entity)
Create
Find(int)
Remove(Entity)
SqlQuery
Aggiunge tuna entità al context, in stato unchanged. Non verrebbe
passata su db.
context.ordini.Attach(newOrder)
Crea una nuova istanze dell’entità corrispondente al tipo del set e ne
restituisce il puntatore. L’istanza non è inserita nel set.
var newOrder = context.ordini.Create();
Una la chiave primaria per trovare una entità, provando prima sul
context, se non la trova sul db.
ordini O = context.ordini.Find(maxOrderId2);
Segna l’entità coma Deleted, verrà cancellata dal database con
SaveChanges.
context.ordini.Remove(O);
Query diretta: SQL che interroga il db e ritorna entità del set.
var ordList = context.ordini.SqlQuery("select * from ordini where id >
10").ToListAsync();
Vittorio Maniezzo - University of Bologna
81
Esempio utilizzo DbSet
esempioEntitiesLite1 context = new esempioEntitiesLite1(connString);
// select
foreach (ordini o in context.ordini)
view.textConsole = o.descr;
// insert
ordini newOrder = new ordini();
newOrder.idcliente = 4;
...
context.ordini.Add(newOrder);
context.SaveChanges();
// update, si passa la primary key del record da aggiornare
ordini O = context.ordini.Find(maxOrderId2);
O.descr = "boh";
context.SaveChanges();
// delete, si passa il puntatore al record da cancellare
O = context.ordini.Find(maxOrderId);
context.ordini.Remove(O);
context.SaveChanges();
Vittorio Maniezzo - University of Bologna
82
41
09/10/2015
Interrogazioni dirette al db
(raw queries)
SQL query per entità:
Il metodo SQLQuery() del DBSet permette di scrivere SQL queries che ritornano istanze di entità.
using (var context = new esempioEntities() )
{ var ordList = context.ordini.SqlQuery("Select * from ordini").ToListAsync();
}
SQL query per tipi non entità:
Se la query SQL ritorna tipi base, non entità, si può usare il metodo SqlQuery sulla classe Database.
Esempio per recuperare una stringa
using (var ctx = new esempioEntities())
{ string descr =
ctx.Database.SqlQuery<string>("Select descr from ordini where id=1").FirstOrDefaultAsync().Result;
}
Altri comandi SQL al database:
Il metodo ExecuteSqlCommnad serve a inviare comandi non-query al database (Insert, Update o Delete)
using (var ctx = new esempioEntities())
{
//Update
int numUpdated =
ctx.Database.ExecuteSqlCommand(@"Update clienti set nome ='nuovo nome' where id=1");
//Insert
int numInserted =
ctx.Database.ExecuteSqlCommand(@"insert into clienti(nome) values('nuovo cliente')");
//Delete
int numDeleted = ctx.Database.ExecuteSqlCommand("delete from clienti where id>4");
}
Vittorio Maniezzo - University of Bologna
83
42