sabato 26 maggio 2007

Visual Studio 2005 e Linux/MySql

Ho voluto provare a connettermi a MySql  da Visual Studio 2005.
Avendo a disposizione un server Linux ho tentato l'accoppiata più classica tra le due entità Visual
Studio (ovviamente) sotto Windows che "Lavora" con MySql su Linux.
Quasi in diretta il resoconto della mia esperienza.


Lato Linux / Server MySql
Sul mio server Linux Fedora Core 6  ho installato MySql utilizzando Yum.
Configurazioni:
Sono entrato come root su mysql
Creato un nuovo database.
Impostato i privilegi per il mio utente
GRANT ALL PRIVILEGES ON *.* TO 'utente' IDENTIFIED BY 'password' WITH GRANT OPTION;

Sul file /etc/services ho verificato i servizi:
mysql 3306/tcp # MySQL
mysql 3306/udp # MySQL

Lato Windows / Client

Scaricato ed installato Connector/Net 5.1, questa versione attualmente in beta provvede al driver .net e ad un addon che imposta MySql tra i database selezionabili e gestibili dall'interno di Visual Studio 2005 (il mio è Visual Studio Team Edition For Software Developers).
Scaricato ed installato i MySQL GUI Tools che consentono di gestire l'amministrazione del server MySql con un'interfaccia GUI.

Problema della licenza

MySql è notoriamente distribuito con "doppia licenza" e cioè il tutto è riassumibile con "SE SEI GPL LO SONO ANCHE IO, SE SEI COMMERCIALE LO SONO ANCHE IO".
Personalmente non lo uso e non lo userò mai a scopi commerciali, tutti i miei esperimenti saranno eventualmente pubblicati gratuitamente in formato sorgente, invito però quanti lo utilizzeranno per scopi commerciali a dotarsi di una regolare licenza, contribuiranno con questo allo sviluppo del software libero.

All'opera con Visual Studio!

Ho aperto Visual Studio e ho provato ad aggiungere una nuova "Data Connection".
Ho dovuto lavorare un pò perchè non mi faceva l'autenticazione, è andato quando ho impostato "persistent security info = false" in modo da non memorizzare la password, è un problema prettamente di windows in quanto collegato con Putty al mio Linux monitorizzavo i file di log e non c'erano segnalazioni relative all'autenticazione.
Successivamente la connessione ho generato una nuova tabella, senza tanta fantasia l'ho chiamata tableprova.
Un appunto, consiglio quando si ha a che fare con Linux di utilizzare sempre le minuscole per nomi di campi e tabelle e soprattutto di non utilizzare nomi composti (spazi inseriti) o contenenti caratteri nazionali o non validi.
Per i nomi di tabelle e campi inoltre (ma credo questo valga anche per qualsiasi database) evitiamo di utilizzare parole o nomi riservati
Se avete qualche dubbio questo link fa per voi
Script di creazione della tabella

DROP TABLE IF EXISTS `dbprova`.`tableprova`;
CREATE TABLE `dbprova`.`tableprova` (
`codice`
int(10) unsigned NOT NULL auto_increment,
`descrizione` varchar(
45) default NULL,
`valore`
decimal(10,0) default NULL,
PRIMARY KEY (`codice`)
) ENGINE
=InnoDB DEFAULT CHARSET=latin1;



Ho creato il mio progetto Windows Forms e ho generato una nuova origine dati a partire dalla mia tabella MySql.
Il wizard di VS mi ha aggiunto il dataset  tipizzato al progetto
Tra i controlli ho selezionato ed aggiunto un BindingSource che una volta configurato ha prodotto e aggiunto un TableAdapter per la mia tabella.
Ho aggiunto e collegato al BindingSource un DataGridView, sono andato nella collection delle colonne e impostato il testo da visualizzare nella header
Da "Data Sources" ho trascinato sulla form i 3 campi, il Wizard ha generato un TextBox collegato al BindingSource
Ho aggiunto alla fine un oggetto BindingNavigator che ho collegato al BindingSource.
Fino a questo punto non ho scritto neanche una riga di codice!
Teoricamente ora dovrebbe funzionare, vediamo i problemi che incontro.
Ovviamente non funziona! ma è solo un piccolo problema! (Access denied for user 'xxxx@xxx"  (using password: NO), sono andato sul mio app.config (altrimenti a che servono?) e ho impostato la mia login e la mia password (su cui stendo un velo pietoso), consiglio di farsi una conessione "trusted" in modo tale da non dover scrivere la password in un posto insicuro come il file config.
Ora la connessione funziona e  si visualizzano perfino i dati!
Però ... non aggiorna il database!, vediamo di rimediare ...
Come sospettavo il wizard si è fermato alla select, i 3 comandi di aggiornamento non sono stati generati, anzi per meglio dire il wizard ha generato sbagliato solo l'insert command.
Ho dovuto andare nel TableAdapter (lato designer) .. e aggiornare il metodo InitAdapter.
Codice aggiornato / inserito


this._adapter.TableMappings.Add(tableMapping);
this._adapter.InsertCommand = new MySql.Data.MySqlClient.MySqlCommand();
this._adapter.InsertCommand.Connection = this.Connection;
this._adapter.InsertCommand.CommandText = "INSERT INTO `dbprova`.`tableprova` (`descrizione`, `valore`) VALUES (?descrizione," +
"?valore)";
this._adapter.InsertCommand.CommandType = System.Data.CommandType.Text;
MySql.Data.MySqlClient.MySqlParameter param
= new MySql.Data.MySqlClient.MySqlParameter();
param.ParameterName
= "?descrizione";
param.DbType
= System.Data.DbType.String;
param.IsNullable
= false;
param.SourceColumn
= "descrizione";
this._adapter.InsertCommand.Parameters.Add(param);
param
= new MySql.Data.MySqlClient.MySqlParameter();
param.ParameterName
= "?valore";
param.IsNullable
= false;
param.SourceColumn
= "valore";
param.DbType
= System.Data.DbType.Double;
this._adapter.InsertCommand.Parameters.Add(param);

this._adapter.DeleteCommand = new MySql.Data.MySqlClient.MySqlCommand();
this._adapter.DeleteCommand.Connection = this.Connection;
this._adapter.DeleteCommand.CommandText = "DELETE FROM `dbprova`.`tableprova` WHERE codice = ?codice";
this._adapter.DeleteCommand.CommandType = System.Data.CommandType.Text;
param
= new MySql.Data.MySqlClient.MySqlParameter();
param.ParameterName
= "?codice";
param.DbType
= System.Data.DbType.String;
param.IsNullable
= false;
param.SourceColumn
= "codice";
param.SourceVersion
= System.Data.DataRowVersion.Original;
this._adapter.DeleteCommand.Parameters.Add(param);

this._adapter.UpdateCommand = new MySql.Data.MySqlClient.MySqlCommand();
this._adapter.UpdateCommand.Connection = this.Connection;
this._adapter.UpdateCommand.CommandText = "UPDATE `dbprova`.`tableprova` SET `descrizione` = ?descrizione, `valore` = ?valore WHERE `codice` = ?codice";
this._adapter.UpdateCommand.CommandType = System.Data.CommandType.Text;
param
= new MySql.Data.MySqlClient.MySqlParameter();
param.ParameterName
= "?descrizione";
param.DbType
= System.Data.DbType.String;
param.IsNullable
= true;
param.SourceColumn
= "descrizione";
this._adapter.UpdateCommand.Parameters.Add(param);
param
= new MySql.Data.MySqlClient.MySqlParameter();
param.ParameterName
= "?valore";
param.IsNullable
= false;
param.SourceColumn
= "valore";
param.DbType
= System.Data.DbType.Double;
this._adapter.UpdateCommand.Parameters.Add(param);
param
= new MySql.Data.MySqlClient.MySqlParameter();
param.ParameterName
= "?codice";
param.DbType
= System.Data.DbType.String;
param.IsNullable
= false;
param.SourceColumn
= "codice";
param.SourceVersion
= System.Data.DataRowVersion.Original;
this._adapter.UpdateCommand.Parameters.Add(param);

Come si può vedere ho inserito del tutto i comandi UpdateCommand e DeleteCommand e ho aggiornato InsertCommand.



  1. Se si vuole il tableadapter ed utilizzare il wizard bisogna che ci accontentiamo di quello che passa il convento :))
  2. Essendo ancora una beta per Visual Studio personalmente mi accontento (però verifico che sia stato segnalato che il wizard non funziona).
  3. Come "segnaposto" ho utilizzato ?nome come indicato dall'help che accompagna il driver, con ? comunque va in errore, originariamente il segnaposto era indicato con il nome del campo senza ?, ho fatto una prova e non ha funzionato
  4. Visto che si è aggiunto / cambiato del codice ad un modulo del designer prepararsi psicologicamente e materialmente a riportare il codice sul medesimo metodo in caso che il wizard riscriva il codice del tableadapter (salvare il codice!).

Per fare l'aggiornamento (Update del TableAdapter) ho inserito un nuovo bottone nel BindingNavigator e sul suo evento il codice:



private void toolStripButton1_Click(object sender, EventArgs e)
{
this.bindingSource1.EndEdit();
this.tableprovaTableAdapter.Update(this.dbprovaDataSet.tableprova);
}


Rimaneva ancora il problema della chiave "counter" che avevo messo sulla tabella.
Ovviamente per poter recuperare nella riga il valore attribuito automaticamente è necessario intercettare l'evento "RowUpdated" legato al TableAdapter (Adapter che espone il MySqlDataAdapter), ho dovuto pertanto cambiare la visibilità da "private" a "public".
Successivamente ho potuto aggiungere l'evento all'Adapter ed il relativo gestore.


tableprovaTableAdapter.Adapter.RowUpdated += new MySql.Data.MySqlClient.MySqlRowUpdatedEventHandler(Adapter_RowUpdated);
void Adapter_RowUpdated(object sender, MySql.Data.MySqlClient.MySqlRowUpdatedEventArgs e)
{
if (e.StatementType == StatementType.Insert)
e.Row[
"codice"] = tableprovaTableAdapter.Adapter.InsertCommand.LastInsertedId;
}

Come si può notare MySql espone su MySqlCommand una proprietà comodissima "LastInsertedId" che restituisce l'ultimo id attribuito.

Per chi lo desidera è disponibile il progetto di prova, perchè funzioni è necessario inserire la tabella nel database ed editare app.config inserendo i propri dati per la connessione.

1 commento:

Anonimo ha detto...

Scaricato ed installato Connector/Net 5.1, questa versione attualmente in beta provvede al driver .net e ad un addon che imposta MySql tra i database selezionabili e gestibili dall'interno di Visual Studio 2005 (il mio è Visual Studio Team Edition For Software Developers).

Ottimo lavoro, Luciano.
Suggerirei solamente di aggiungere, alla frase di cui sopra, che questo non si verifica nella versione Express.

Chico_