1
ADO.NETed Entity Framework
Vittorio Maniezzo - University of Bologna -
Localserver
Installando Visual Studio viene installata anche una versione di SQL Server, chiamata SQL Server Express o Localserver. Utile per sviluppo e debug. Si può installare anche indipendentemente da VS.
Accessibile con qualunque strumento, fra cui:
• l’utility a riga di comando SqlLocalDB
• https://docs.microsoft.com/en-us/sql/tools/sqllocaldb-utility
• l’utility a riga di comando sqlcmd
• https://docs.microsoft.com/en-us/sql/tools/sqlcmd-utility
• https://docs.microsoft.com/en-us/sql/relational-
databases/scripting/sqlcmd-use-the-utility
• SQL Server Management Studio (SSMS)
• https://docs.microsoft.com/en-us/sql/ssms/download-sql-server-
management-studio-ssms
Vittorio Maniezzo - University of Bologna 2
1
2
2
SqlLocalDb
Utile soprattutto per la gestione del LocalDb, non per query. Notare che LocalDb ha diverse "istanze" al cui interno stanno i database.
sqllocaldb v – elenca le versioni SQL Express installate
sqllocaldb info – elenca le istanze utilizzabili
v11.0 = Microsoft SQL Server 2012 (11.0.3000.0)
v12.0 = Microsoft SQL Server 2014 (12.0.2000.8)
MSSqlLocalDb = Microsoft SQL Server 2016 (13.0.1601.5)
sqllocaldb info MSSQLLocalDB – tutti i dati di una istanza (pipe)
sqllocaldb s MSSQLLocalDB – per avviare una istanza bloccata sqllocaldb p MSSQLLocalDB – per bloccare una istanza attiva
sqlLocalDb create "testInst" –s – per creare ed avviare una istanza
sqlLocalDb delete "testInst" – per cancellare una istanza (bloccata)
Con l’istanza bloccata si possono copiare i file .mdf e .ldf che contengono un database di interesse interno a quella istanza.
Vittorio Maniezzo - University of Bologna 3
sqlcmd
Sqlcmd lavora su localserver via ODBC, connettendosi all'istanza attiva. Permette di inoltrare qualunque comando T-SQL a SQL Sever(express o normale) da file come fosse una stored procedure o da riga di comando.
Nel mio computer, attualmente è nella cartella:
C:\Program Files\Microsoft SQL Server\110\Tools\Binn\
sqlcmd -S (LocalDb)\MSSqlLocalDb
sqlcmd -S (LocalDb)\MSSqlLocalDb -i "c:\temp\creaDb.sql"
sqlcmd -S (LocalDb)\MSSqlLocalDb -Q "CREATE/DROP DATABASE testDb"
Per elencare tutti i db nell'istanza:sqlcmd -S (LocalDb)\MSSqlLocalDb -Q "select name from sys.databases"
Vittorio Maniezzo - University of Bologna 4
3
4
3
sqlcmd
Per caricare il db da filesqlcmd -S (LocalDB)\mssqllocaldb
Poi, dentro > USE [master]
> GO
> CREATE DATABASE [database_name] ON
> ( FILENAME = N'C:\temp\test\testDb.mdf' ),
> ( FILENAME = N'C:\temp\test\testDb_log.ldf' )
> FOR ATTACH ;
> GO
Vittorio Maniezzo - University of Bologna 5
Sqlcmd - tablesuse testDbGOIF OBJECT_ID('clienti', 'U') IS NOT NULL DROP TABLE clienti;CREATE TABLE clienti (
[PK_UID] INT NOT NULL IDENTITY(0,1) PRIMARY KEY,[id] INTEGER,[nome] NVARCHAR(MAX));
INSERT INTO clienti VALUES(1,'pippo');INSERT INTO clienti VALUES(2,'pluto');INSERT INTO clienti VALUES(3,'topolino');INSERT INTO clienti VALUES(4,'minnie');IF OBJECT_ID('ordini', 'U') IS NOT NULL DROP TABLE ordini;CREATE TABLE ordini (
[PK_UID] INT NOT NULL IDENTITY(0,1) PRIMARY KEY,[id] INTEGER,[idcliente] INTEGER,[codice] INTEGER,[descr] NVARCHAR(MAX));
INSERT INTO ordini VALUES(1,1,10,'dieci');INSERT INTO ordini VALUES(2,1,11,'undici');INSERT INTO ordini VALUES(3,1,12,'dodici');INSERT INTO ordini VALUES(4,2,20,'venti');INSERT INTO ordini VALUES(5,2,21,'ventuno');INSERT INTO ordini VALUES(6,2,22,'ventidue');INSERT INTO ordini VALUES(7,3,30,'trenta');INSERT INTO ordini VALUES(8,3,31,'trentuno');INSERT INTO ordini VALUES(9,4,40,'quaranta');INSERT INTO ordini VALUES(10,4,41,'quarantuno');INSERT INTO ordini VALUES(11,4,42,'quarantadue');INSERT INTO ordini VALUES(12,4,43,'quarantatre');INSERT INTO ordini VALUES(13,4,44,'quarantaquattro');
Vittorio Maniezzo - University of Bologna 6
5
6
4
Sqlcmd – backup e restore
Per salvare un backupSqlCmd -E -S Server_Name –Q “BACKUP DATABASE [Name_of_Database] TO DISK=’X:PathToBackupLocation[Name_of_Database].bak'”
esempio:SqlCmd -E -S MyServer –Q “BACKUP DATABASE [MyDB] TO DISK=’D:BackupsMyDB.bak'”
Per ripristinare un backupSqlCmd -E -S Server_Name –Q “RESTORE DATABASE [Name_of_Database] FROM DISK=’X:PathToBackupFile[File_Name].bak'”
esempio:SqlCmd -E -S MyServer –Q “RESTORE DATABASE [MyDB] FROM DISK=’D:BackupsMyDB.bak'”
Nota: questi per stessa istanza di SS. Per server diversi usare SSMS.
Vittorio Maniezzo - University of Bologna 7
SSMS
Possibile con nome server (localdb)\MSSQLLocalDB
SSMS molto pesante ma gratuito e potente.
Vittorio Maniezzo - University of Bologna 8
7
8
5
Esempio: anagrafica ordiniDue tabelle: clienti e ordini:
Vittorio Maniezzo - University of Bologna - 9
Clienti
id nome
1 pippo
2 pluto
3 topolino
4 minnie
Ordini
id idcliente codice descr
1 1 10 dieci
2 1 11 undici
3 1 12 dodici
4 2 20 venti
5 2 21 ventuno
6 2 22 ventidue
7 3 30 trenta
8 3 31 trentuno
9 4 40 quaranta
10 4 41 quarantuno
11 4 42 quarantadue
12 4 43 quarantatre
13 4 44 quarantaquattro
CREATE TABLE clienti
(id integer primary
key autoincrement,
nome text)
CREATE TABLE ordini (id integer primary key autoincrement,
idcliente integer, codice integer, descr text, foreign
key(idcliente) references clienti(id))
Creazione – ver. SqLite
DROP TABLE if exists clienti;
CREATE TABLE clienti(id integer primarykey autoincrement,nome text);
INSERT INTO clienti VALUES(1,'pippo');INSERT INTO clienti VALUES(2,'pluto');INSERT INTO clienti VALUES(3,'topolino');INSERT INTO clienti VALUES(4,'minnie');
DROP TABLE if exists ordini;
CREATE TABLE ordini (id integer primary key autoincrement,idcliente integer, codice integer, descr text, foreignkey(idcliente) references clienti(id));
INSERT INTO ordini VALUES(1,1,10,'dieci');INSERT INTO ordini VALUES(2,1,11,'undici');INSERT INTO ordini VALUES(3,1,12,'dodici');INSERT INTO ordini VALUES(4,2,20,'venti');INSERT INTO ordini VALUES(5,2,21,'ventuno');INSERT INTO ordini VALUES(6,2,22,'ventidue');INSERT INTO ordini VALUES(7,3,30,'trenta');INSERT INTO ordini VALUES(8,3,31,'trentuno');INSERT INTO ordini VALUES(9,4,40,'quaranta');INSERT INTO ordini VALUES(10,4,41,'quarantuno');INSERT INTO ordini VALUES(11,4,42,'quarantadue');INSERT INTO ordini VALUES(12,4,43,'quarantatre');INSERT INTO ordini VALUES(13,4,44,'quarantaquattro');
Vittorio Maniezzo - University of Bologna 10
9
10
6
ADO.NETPrima, 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
ADO
ODBC Provider Simple Provider Native Provider
OLE DB Provider
ODBC
ODBC Driver
TextFile
Database Database
OLE DB Provider
Mainframe
OLE DB
Applicazione utente
Vittorio Maniezzo - University of Bologna
11
12
7
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
SQL Managed Provider
SQL ServerDatabase
ADO.NET Managed Providers
ADO.NET Managed Provider
ADO Managed Provider
OLE DB Provider
Database
Applicazione utente
Vittorio Maniezzo - University of Bologna
13
14
8
DataSet
SQL Server .NET
Data Provider
OLE DB .NET
Data Provider
SQL Server OLEDB source
Un data provider .net è un insieme di classi che servono a connettersi a una sorgente dati per ottenerne e modificarne i dati.
Vittorio Maniezzo - University of Bologna
Managed Provider
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
15
16
9
Architettura
DataSet
Command
ConnectionConnection
Command
DataReader
DBMS
Strutture dati personali
Managed
Data
Provider
Data
Store
Vittorio Maniezzo - University of Bologna
Oggetti di un data provider
Oggetto Descrizione
Connection Stabilisce una connessione con una specifica sorgente dati.
Command Esegue un comando sulla sorgente dati. Esponedei Parameters e accetta Transaction da unaConnection.
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
17
18
10
Architettura
Vittorio Maniezzo - University of Bologna
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
19
20
11
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
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
21
22
12
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
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
23
24
13
Classi ADO.NET Interfaccia IDbTransaction
Vittorio Maniezzo - University of Bologna 25
Esempio di due comandi (insert e select) in un’unica transazioneusing (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 comandoIDStorico=(int) command.ExecuteScalar();transaction.Commit(); // o entrambi i comandi o nessuno
}con.Close();
}
catch(Exception ex) { ...
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 IDbConnectionsono bloccate finchè il reader non viene chiuso
Vittorio Maniezzo - University of Bologna
25
26
14
DBMS
ADO.NET, utilizzo connesso
In un contesto connesso, le risorse sono mantenute sulserver finchè non viene chusala connessione
Operazioni
1. Apri la connessione
2. Esegui un comando
3. Elabora le righe nel reader
4. Chiudi il reader
5. Chiudi la connessione
IDbConnection
IDbCommand
IDbDataReader
Vittorio Maniezzo - University of Bologna
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
27
28
15
Stringhe connessionev. 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=***";
connectionString = @"Data Source=(LocalDB)\MSSQLLocalDB;Initial
Catalog = testDb;Integrated Security = True;"
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
connString = "Server=localhost;UID=studenti;
PWD=laPWD;Database=studenti;Port=5432;";Vittorio Maniezzo - University of Bologna
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=|DataDirectory|\DBFILE;Version=3;" providerName="System.Data.SQLite" />
<add name="LocalDbConn" connectionString="Data Source=(LocalDB)\MSSQLLocalDB;Initial Catalog = testDb;Integrated Security = True;Connect Timeout=20" providerName="System.Data.SqlClient" />
</connectionStrings>
Vittorio Maniezzo - University of Bologna 30
29
30
16
IstanziazioneIn app.config:<appSettings>
<add key="dbServer" value="LocalDbConn" />
<!-- "SQLiteConn" "LocalDbConn" "RemoteSqlServConn"-->
</appSettings>
Nel costruttore della classe che istanzia:string sdb = ConfigurationManager.AppSettings["dbServer"];
switch (sdb)
{ case "SQLiteConn":
connString = ConfigurationManager.ConnectionStrings["SQLiteConn"].ConnectionString;
factory = ConfigurationManager.ConnectionStrings["SQLiteConn"].ProviderName;
break;
case "LocalDbConn":
connString = ConfigurationManager.ConnectionStrings["LocalSqlServConn"].ConnectionString;
factory = ConfigurationManager.ConnectionStrings["LocalSqlServConn"].ProviderName;
break;
. . .Vittorio Maniezzo - University of Bologna 31
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 32
31
32
17
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 33
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 collectionParameters del comando:
com.Parameters.Add(param);
Vittorio Maniezzo - University of Bologna 34
33
34
18
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 35
Indipendenza dalla tecnologia
Vittorio Maniezzo - University of Bologna 36
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 namespaceSystem.Data.Common, come nel seguito.
Oppure usare un ORM (v. ancora dopo)
35
36
19
DbProviderFactories
Vittorio Maniezzo - University of Bologna 37
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.
DbProviderFactories
Vittorio Maniezzo - University of Bologna 38
Per usare DbProviderFactories è necessario che il provider sia noto al sistema.Se 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).
37
38
20
Classi ADO.NET Esempio DataReader (SqLite, con provider factory)
DbProviderFactory dbFactory = DbProviderFactories.GetFactory(factory);
using (DbConnection conn = dbFactory.CreateConnection())
{
try
{
conn.ConnectionString = connString;
conn.Open();
DbCommand com = conn.CreateCommand();
com.CommandText = queryText;
DbDataReader reader = com.ExecuteReader();
while (reader.Read())
FlushText(this, reader["id"] + " " + reader["nome"]);
reader.Close();
conn.Close();
}
catch (Exception ex)
{
FlushText(this, "[dataReader] Error: " + ex.Message);
}
finally
{
if (conn.State == ConnectionState.Open) conn.Close();
}
}
Vittorio Maniezzo - University of Bologna
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
39
40
21
Classi ADO.NET DataSet
DataSet
DataTable
DataRelation
DataRow
DataColumn
Vittorio Maniezzo - University of Bologna
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
41
42
22
ADO.NET utilizzo disconnessoIn un contesto disconnesso, le risorse NON sono gestite sulserver quado i dati vengonoelaborati
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
IDbConnection
IDbDataAdapter
DataSet
DBMS
Vittorio Maniezzo - University of Bologna
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
43
44
23
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
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
45
46
24
Classi ADO.NET DataRelation
Correla due DataTables via DataColumns
I valori di DataType delle due DataColumnsdevono essere identici
Le modifiche non corrispondenti alla relazione sono proibite
Vittorio Maniezzo - University of Bologna
DataSet
DataRelation
DataRelationCollection
DataTableCollection
DataSet
DataTable
DataRowCollection
DataColumnCollection
DataView
DataRow
DataColumn
Vittorio Maniezzo - University of Bologna
47
48
25
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
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
49
64
26
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
Object Relational Mapping (ORM)
Vittorio Maniezzo - University of Bologna
65
66
27
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
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
67
68
28
ORM Mapping – EsempioEsempio di mapping per un sottinsieme del database Northwind
schema database relazionale
ORM Entities (Classi C#)
ORM
Framework
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
69
70
29
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
Entity Framework: Architettura
Vittorio Maniezzo - University of Bologna
72
73
30
EF: componenti architettura
Vittorio Maniezzo - University of Bologna 75
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 database, incude tabelle, viste, stored procedures, oltre cherelazioni e chiavi (primarie, esterne, … ).
• Mapping: contiene le informazioni su come gli elementidel modello concettuale sono correlate a quelli dellostorage.
EF: componenti architettura
Vittorio Maniezzo - University of Bologna 76
Interrogazione, può avvenire con tre modalità:
• LINQ to Entities: LINQ to Entities è un linguaggio di query che può interrogare l’object model. Ritorna delleentità, definite nel modello concettuale.
• Entity SQL: Entity SQL è un altro linguaggio di query, molto simile a sql, che permette di lavoraredirettamente su collezioni di entità.
POCO, Plain Old CLR Objects (!), lavora sulle collezionidi entità dell’object model con gli operatori c# standard.
75
76
31
EF: componenti architettura
Vittorio Maniezzo - University of Bologna 77
Object Service: è l’entry point del codice utente per accedere aidati 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 ilprovider ADO.Net che fa da interfaccia al database.
ADO.Net Data Provider: comunica con il database usando ADO.Net.
Sia VS2017 che EF6 sono tecnologie recenti e la loro integrazione con strumenti non MicroSoft non è ancora molto robusta (con SQL server pochi problemi).
Per accedere al database SqLite con EF6 da VisualStudio 2017 procedure come segue.
1 – Installare la versione SqLite adeguata.
2 – Controllare che fra le reference del progetto ci sia Entity Framework. Se non c'è, installarla via NuGet (manage packages for solution).
3 – Reference (copia) di System.Data.SQLite.dll, System.Data.SQLite.EF6.dll, System.Data.SQLite.Linq.dll
4 – Controllare che in app.config fra i provider ci sia<providers>
<provider invariantName="System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
VS2017 e EF6: definizione del progetto
Vittorio Maniezzo - University of Bologna
77
78
32
VS2017 e EF6: definizione del progetto
Vittorio Maniezzo - University of Bologna 79
5 – In VS2017, clickare col destro sul progetto (in solution explorer) e selezionare “aggiungi / Nuovo elemento”.
6 – Dal wizard dell’Add new item, selezionare “ADO.NET Entity Data Model”. Questo attiva il wizard dell’Entity data model.
7 – Selezionare “Entity framework designer da database”
8 – selezionare “Nuova connessione”
9 - selezionare il provider per SQLite (se non c’è è stato installato ilprovider sbagliato. Tornare al passo 1).
10 -cliccare su browse e selezionare il file sqlite da utilizzare.
11 -selezionare gli elementi del database da includere nel modello
12 - fine, il modello viene generato automaticamente
Entity Data Model (EDM)
Vittorio Maniezzo - University of Bologna 80
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, checontiene le tre sezioni principali SSDL (storage model), CSDL (conceptual model) e C-S mapping.
Anche il file app.config vienemodificato aggiornando le sezioniopportune (configSections, startup, connectionStrings,
entityFramework)
79
80
33
Model browser
Vittorio Maniezzo - University of Bologna 81
Cliccando col destro sul diagramma del modelloconcettuale 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 sulletabelle del DB.
• Tipi complessi: Classi generate dall’ EDM come risultato di stored procedures, funzioni che generanotabelle, ecc.
• Tipi enum: lista di tutte le entità usate come enumdall’ entity framework.
• Associazioni: relazioni foreign key fra entità.
• Importazioni di funzioni: funzioni che sarannomappate sulle stored procedures, ecc. E cje quindisaranno usate come funzioni dall’ EF.
• esempioModel.Store: lo schema del database (SSDL).
Approcci progettuali
Vittorio Maniezzo - University of Bologna 82
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.
81
82
34
DbContext
Vittorio Maniezzo - University of Bologna 83
Creando un EDM viene generata una classe (qui 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à chesono mappate su tabelle del db.
• Query: converte le query L2E, … , in SQL e le inoltra al database.
• Change Tracking: memorizza i cambiamenti subiti dale entità dopoessere state lette dal DB.
• Persisting Data: Esegue Insert, Update e Delete sul DB in funzione dellostato dele entità.
• Caching: delle entità lette.
• Gestione delle relazioni: responsabile del controllo delle relazioni.
• Object Materialization: converte i dati delle tabelle in oggetti (entità).
DbSet
Vittorio Maniezzo - University of Bologna 84
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 DbContextcorrisponde 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.
83
84
35
Metodi del DbSet (i principali)
Vittorio Maniezzo - University of Bologna 85
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)Aggiunge una entità al context, in stato unchanged. Non verrebbe passata su db.context.ordini.Attach(newOrder)
CreateCrea una nuova istanza dell’entità corrispondente al tipo del set e ne restituisce il puntatore. L’istanza non è inserita nel set.var newOrder = context.ordini.Create();
Find(int)Una la chiave primaria per trovare una entità, provando prima sul context, se non la trova sul db. ordini O = context.ordini.Find(maxOrderId2);
Remove(Entity)Segna l’entità coma Deleted, verrà cancellata dal database con SaveChanges.context.ordini.Remove(O);
SqlQueryQuery diretta: SQL che interroga il db e ritorna entità del set. var ordList = context.ordini.SqlQuery("select * from ordini where id > 10").ToListAsync();
Esempio utilizzo DbSettestDbEntities context = new testDbEntities (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 86
85
86
36
Interrogazioni dirette al db(raw queries)
Vittorio Maniezzo - University of Bologna 87
SQL query per collezioni entità (using System.Linq):Il metodo SQLQuery() del DBSet permette di scrivere SQL queries che ritornano istanze di entità. using (var context = new testDbEntities () )
{ var lstOrdini = context.ordini.SqlQuery("Select * from ordini").ToList();
}
SQL query per tipi base (non entità), non serve Linq:Se la query SQL ritorna tipi base, non entità, si può usare il metodo SqlQuery sulla classe Database.Esempio per recuperare un interousing (var ctx = new testDbEntities ())
{ maxOrderId =
ctx.Database.SqlQuery<int>("Select max(PK_UID) from ordini").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 testDbEntities ())
{
//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");
}
87