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.

giovedì 24 maggio 2007

Ottenere i files ordinati per data - utilizzo di IComparer e GetFiles

Oggi un post passato in un forum che frequento mi ha incuriosito e allora ho preparato questa soluzione che dimostra come si può realizzare un ordinamento per data ultima modifica dei files presenti in una certa cartella.

Il tutto passa attraverso Array.Sort a cui vengono passati come parametri, l'array dei nomi dei file ottenuto con GetFiles, un metodo della classe System.IO, e l'implementazione dell'interfaccia IComparer e del suo metodo Compare.

Esempio C#
Esempio VB.NET

Gestire le colonne di tipo DataGridViewButtonColumn

Su DataGridView è possibile aggiungere dei "Button" e gestire l'evento click.
Per farlo è sufficiente

  1. Andare nelle proprietà del DataGridView ed aggiungere una nuova colonna alla collection Columns.
  2. Scegliere ovviamente il tipo di colonna come "DataGridViewButtonColumn"

La gestione dell'evento click è effettuata dalla collection Cells sottostante, è sufficiente sottoscrivere l'evento "CellContentClick" e applicare la propria implementazione.

Esempio:

 

private void DataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
DataGridViewRow riga
= DataGridView1.Rows[e.RowIndex];
if (DataGridView1.Columns[e.ColumnIndex].Equals(this.Column1))
MessageBox.Show(String.Format(
"Intercetto Riga {0} - Valore {1}", e.RowIndex, riga.Cells[2].Value));
}

Esempio C#
Esempio VB.NET

domenica 20 maggio 2007

Disabilitare il riordino Ascendente / Discendente sul DataGridView

Cliccando sulla header del DataGridView si ottiene il riordino dei dati.
Per impedire che venga fatto uno dei metodi è intercettare il click e interrompere l'azione di riordino.
Per farlo ho aggiunto un evento MouseMove sul DataGridView

C#

private void dataGridView1_MouseDown(object sender, MouseEventArgs e)
{

DataGridView.HitTestInfo ht
= dataGridView1.HitTest(e.X, e.Y);
if ((ht.Type == DataGridViewHitTestType.ColumnHeader) && (ht.ColumnIndex == 0))
MessageBox.Show(
"Ordinamento vietato");
}


VB.NET


Private Sub dataGridView1_MouseDown(ByVal sender As System.Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles dataGridView1.MouseDown
Dim ht As DataGridView.HitTestInfo
= dataGridView1.HitTest(e.X, e.Y)
If ((ht.Type
= DataGridViewHitTestType.ColumnHeader) AndAlso (ht.ColumnIndex = 0)) Then
MessageBox.Show(
"Ordinamento vietato")
End If
End Sub


Spiegazione:
HittestInfo è un oggetto che espone la proprietà "Type" relativa alla enumerazione DataGridViewHittestType che elenca alcuni degli oggetti costituenti la DataGridView.
Utilizziamo HittestInfo generato dalle coordinate del punto cliccato per determinare se si è cliccata una header.
HittestInfo espone anche la proprietà ColumnIndex che espone il numero della colonna cliccata.

mercoledì 16 maggio 2007

Funzionamenti diversi per campi Code e campi Text su Dynamics Nav

Con un campo di tipo Text l'espressione: 
campoText := ''; // l'inizializzazione lo genera così
campoText += '0001';
Corrisponde con:
campoText := campoText + '0001';
Orbene con un campo di tipo Code che notoriamente è un alfanumerico che contiene solo i caratteri maiuscoli l'istruzione:
campoCode := ''; // l'inizializzazone lo genera così
campoCode += '0001';
stranamente non funziona (o meglio il contenuto della variabile campoCode rimane inalterato), invece:
campoCode := campoCode + '0001';
Funziona perfettamente.
Qualcuno sa perchè?

domenica 13 maggio 2007

Utilizzo di Path.Combine

Magari scopro l'acqua calda ma .. non mi sembra.
Il Framework .net sulla classe Path contiene un metodo che semplifica di molto la costruzione dei nomi di files e di cartelle.

Ovviamente Path.Combine(elemento1,elemento2) serve a generare una path aggregando due elementi diversi che di solito sono un percorso ed il nome di un file.
Utilizzo:
VB.NET
Dim myFile As String = Path.Combine("C:\temp","prova.txt")
C#
string myFile = Path.Combine(@"C:\temp","prova.txt");
in entrambi i casi myFile conterrà "C:\temp\prova.txt"

Ovviamente ci sono delle limitazioni e dei caratteri non accettati come nome di file o cartella.
Si consiglia pertanto di richiamare questo metodo in un blocco di try/catch.

Selezionare un elemento ListBox con il tasto destro del mouse

 

Come è noto la selezione degli elementi su ListBox può essere "personalizzata" impostando la proprietà SelectionMode con uno dei valori della lista (enum) "None,One,MultiSimple,MultiExtended".
Tralasciando None e One non voglio pensare che non si possa intuire cosa significhino!, gli altri due permettono la selezione multipla degli elementi, la differenza tra i due è che MultiExtended permette la multiselezione (come MultiSimple) anche mediante l'utilizzo dei tasti Ctrl (control) e Shift.
Per la nostra piccola dimostrazione ho utilizzato l'impostazione "MultiExtended".
Per poter selezionare anche con il click del pulsante destro è necessario identificare l'elemento puntato, ho aggiunto un evento "mousedown" ed il relativo gestore.

private void listBox1_MouseDown(object sender, MouseEventArgs e)
{
    // verifichiamo che si tratti proprio del pulsante destro
   if ((e.Button & MouseButtons.Right) == MouseButtons.Right)
   { 
      // generiamo un nuovo oggetto Point alle coordinate cliccate dal mouse
      Point p = new Point(e.X, e.Y);
      for (int k = 0; k < listBox1.Items.Count; k++)
      {
         // se l'area occupata dall'elemento contiene l'elemento puntato
         // lo inseriamo tra gli elementi selezionati
         if (listBox1.GetItemRectangle(k).Contains(p))
        {
            listBox1.SelectedIndex = k;
            // rigeneriamo il listbox2 mettendoci tutti gli elementi selezionati
            listBox2.Items.Clear();
           foreach (object ob in listBox1.SelectedItems)
               listBox2.Items.Add(ob);
           break;
        }
    }
}

Esempio VB.NET

Esempio C#

giovedì 10 maggio 2007

Verificare se un file è scrivibile con C# e VB.NET

Fare la verifica se un determinato file è scrivibile è alquanto semplice.
E' sufficiente "tentare" di aprire il file in modo esclusivo, l'operazione può avere due risultati:

  • si ottiene una eccezione, il file è già stato aperto in modo esclusivo da altri
  • si riesce ad aprire, in questo modo il file è "leggibile", il test file.CanWrite ci permette di sapere se è anche scrivibile

Nel caso il file sia scrivibile non è detto che non sia in uso da altri processi che possono avere aperto il file in modo non esclusivo oppure utilizzano GetTempFileName per modificare il file.

Esempio in C#
Esempio in VB.NET

giovedì 3 maggio 2007

Archivio Sportelli Bancari - elenco Abi Cab Aggiornato ad Aprile 2007

L'archivio Sportelli Bancari Abi Cab aggiornato ad Aprile 2007  è disponibile per lo scaricamento.
Alcune informazioni sui files:
- campi separati da tabulazione hex: 09
- righe separate dal terminatore di riga windows hex: 0d0a
- intestazione colonne sulla prima riga
Il file TabAbi contiene i dati relativi alle banche
Il file TabAbiCab contiene le informazioni relative agli sportelli
Nel file TabAbi e TabAbiCab è presente il campo "Aggiornamento" che evidenzia la data di riferimento di validità dei dati.
Tutte le righe di TabAbiCab che NON contengono la data più recente sono da considerarsi relative a sportelli non più attivi o assorbiti da altre banche, tali sportelli sono presenti solo come log dei dati storici, ne è sconsigliato l'utilizzo per nuove emissioni di flussi di comunicazione con le banche.

martedì 1 maggio 2007

Piccola escursione su OleDbParameter e DbNull con C#

Perchè si possano inviare e vengano accettati valori nulli nei parametri che inviamo al database tramite un OleDbCommand è necessario che ci siano alcuni presupposti:

  • Che nel database siano accettati i valori nulli
  • Che nella definizione dei parametri sia impostato IsNullable = true

In mancanza il nostro "gioco" non funzionerà.
Per fare la prova ho inserito una nuova tabella su un database di access con 5 campi di diverso tipo:

  • intero
  • decimale
  • testo
  • data
  • contatore

Il campo contatore è la pk, non accettando nulli non è neanche citato nella prova.
L'esempio dimostra come si possono inserire e far accettare dal database valori nulli su ciascun parametro.

Purtroppo ci sono dei problemi a passare campi decimali, ho ovviato definendo il campo come double.