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