in

DotNetSide

Dot Net South Italy Developers User Group

Tips

C#

  • DataObjectAttribute e ObjectDataSource

    Autore: Mario Ferrante

    L’attributo DataObject rende più facile la ricerca delle classi che si vogliono legare ad un controllo DataBound durante la configurazione dell’ObjectDataSource.
    Quando si utilizza l’ObjectDataSource, la prima cosa che devo fare è scegliere l’oggetto Business da utilizzare per recuperare o gestire i dati e per legarli ad un controllo DataBound:

    Purtroppo se chiedo al wizard dell’ObjectDataSource di mostrarmi solo i “Data Components” (come in figura), la maggior parte delle volte il risultato che ottengo è un menù a discesa vuoto.
    Dunque non rimane che levare la spunta a “Show only data components” e cercare l’oggetto Business che mi interessa tra tutti quelli a cui è referenziata la mia applicazione, come si vede nella figura successiva.

     

    Poco male nel caso di piccole applicazioni, ma nel caso di applicazioni molto grandi o che comunque referenziano molti assemblies il problema diventa un po’ più serio.
    Ed è qui che viene in aiuto l’attributo DataObjectAttribute o semplicemente DataObject. Questo attributo può essere usato a livello di classe, ma anche a livello dei singoli metodi.

    L’Attributo DataObject a livello di classe
    Supponiamo che nella mia applicazione ho una classe che mi gestisce il CRUD (Create Retrieve Update e Delete) di News a cui aggiungo, a livello di classe, l’attributo DataObject:

     

     

    Imports System.ComponentModel Namespace Mario.DotNetSide <DataObject()> _ Public Class NewsManager 'Implementazione della Classe End Class End Namespace

     

    Il risultato sarà:

    Il costruttore dell’attributo DataObject accetta anche un parametro di tipo System.Boolean, se False dichiaro esplicitamente all’Object Data Source di non considerare quella classe come Data Component.
    L’Attributo DataObject a livello di metodo

    Abbiamo usato questo attributo a livello di Classe, ma lo posso utilizzare anche a livello di metodi per definire quali di essi devono essere utilizzati per selezionare i dati, quali per inserire un record, cancellarlo o modificarlo:

     

    <DataObject()> _ Public Class NewsManager <DataObjectMethod(DataObjectMethodType.Select, True)> _ Public Function GetNews() As NewsCollection End Function <DataObjectMethod(DataObjectMethodType.Insert, True)> _ Public Function AddNews(ByVal item As News) As Integer End Function End Class

    I valori dell’enumerazione DataObjectMethodType sono autoesplicativi:

    • Delete
    • Fill
    • Insert
    • Select
    • Update

    Il secondo parametro booleano passato all’attributo (True in questo caso) indica all Object Data Source se considerare (True) o meno (False) quel metodo per una determinata funzione (ad esempio di Select).
    In .Net gli attributi hanno una fondamentale importanza, essi contribuiscono a fornire tutte quelle informazioni che costituiscono i MetaData di un’assembly. Inoltre possono influenzare il comportamento di un oggetto sia a run-time sia (come in questo caso) a design-time.

  • Recuperare i dati da una cella del DataGridView

     

    Autore: Michele Locuratolo

    Spesso si ha la necessità di recuperare i dati da una cella di una DataGrid. La regola vuole che tali elementi vengano recuperati dal DataSource e non dalla DataGridView in quanto, come spesso accade, nella gliglia potrebbero non essere mostrati tutti i dati.
    Per farlo, usiamo il seguente codice:


    1    private object GetCurrentBindedObject(DataGridView dgv) {
    2
    3 if (dgv == null || dgv.DataSource == null) {
    4 return null;
    5 }
    6
    7 BindingManagerBase bmb = dgv.BindingContext[dgv.DataSource];
    8
    9 if (bmb == null) {
    10 return null;
    11 }
    12
    13 return bmb.Current;
    14 }

    Il tipo di ritorno è un object che, in caso la sorgente dati sia una DataTable, dovrà essere castato a DataRowView.
    A questo punto, per accedere al dato che ci interessa leggere, possiamo usare il seguente codice:

    1    DataRowView drv = (DataRowView)GetCurrentBindedObject(dgvUsers); 
    2 string strNome = drv["Nome"].ToString();


    Il .NET Framework 2.0 però, ci permette di creare in modo semplice delle collection di oggetti bindabili ad elementi della UI.
    Usando questa nuova caratteristica ed i comodi Generics, possiamo modificare il codice di GetCurrentBindedObject per farci ritornare direttamente il tipo che ci interessa.
    Supponiamo di avere un oggetto User che rappresenta l’utente e la collection Users, che deriva da BinsingList<T> che rappresenta l’elenco dei nostri utenti (vedi codice allegato).
    Il nostro codice può essere modificato come segue:


    1    private T GetCurrentBindedObject(DataGridView dgv) where T: class {
    2
    3 if (dgv == null || dgv.DataSource == null) {
    4 return null;
    5 }
    6
    7 BindingManagerBase bmb = dgv.BindingContext[dgv.DataSource];
    8
    9 if (bmb == null) {
    10 return null;
    11 }
    12
    13 return bmb.Current as T;
    14 }


    da usare con il seguente codice:

    1    User user = GetCurrentBindedObject(dgvUsers);
    2 string Nome = user.Name;

     Lo stesso metodo, essendo generico, potrà essere usato per tutti gli object del nostro domain model.
    Il codice completo è disponibile nel file allegato.

    Posted Jun 30 2006, 11:36 PM by VitoA with 1 comment(s)
    Filed under:
  • StringBuilder vs Concatenamento

     

    Autore: Michele Locuratolo

    Mi capita spesso che mi venga richiesto come fare per migliorare le performance di una applicazione e, altrettanto spesso, scopro che viene usato il semplice concatenamento di stringhe per comporre dei messaggi. Si va dal semplice messaggio di poche parole (che concatena 4 o 5 stringhe) a veri e propri testi.
    Questo modo di fare causa spesso problemi di performance. Scopriamo insieme il perchè.
    Usiamo allo scopo questo semplice codice:

     string myString = string.Empty;
     
    for (int i = 0; i < 10000; i++) {
         myString += i.ToString();
     }

    Il funzionamento è estremamente semplice: la nostra stringa sarà il risultato del concatenamento di 10.000 numeri.
    Internamente però, questa operazione è decisamente pesante. Partendo dal concetto che una stringa è un oggetto immutabile e che, ad ogni modifica di essa, ne deve essere creata una copia che contiene il nuovo valore, non è difficile immaginare cosa accade all'interno del nostro ciclo.
    Al primo "giro", verrà creata una nuova stringa che conterrà il valore precedente (string.Empty) ed il nuovo (0). Al secondo cliclo, ancora una volta, verrà creata una nuova stringa che contenente il valore precedente ed il nuovo (1) e così via. Ne consegue che, per 10.000 cicli, verranno create 10.000 stringhe! Tutte le stringhe precedenti vengono poi eliminate dal Garbage Collector.
    Se mandiamo in esecuzione il codice di sopra aggiungendo un paio di timer, otterremo il seguente risultato:

    Avvio ciclo di concatenamento
    Tempo impiegato: 00:00:00.5107344

    Praticamente 1/2 secondo per concatenare 10.000 stringhe. Apparentemente potrebbe sembrare poco ma vediamo cosa accade usando un oggetto realizzato apposta per questo scopo: lo StringBuilder:

     StringBuilder sb = new StringBuilder(10000);
     
    for (int i = 0; i < 10000; i++) {
         sb.Append(i.ToString());
     }

    Grazie a questo oggetto, il concatenamento non avviene più creando una copia della stringa. StringBuilder contiene un vettore di caratteri ed il concatenamento avviene direttamente sul vettore evitando inutili copie di oggetti. Se mandiamo in esecuzione il codice otterremo:

    Avvio ciclo con StringBuilder
    Tempo impiegato: 00:00:00.0100144

    50 volte più veloce! Ma la velocità è solo la minima conseguenza. Se usiamo un tool come CLR Profiler (esiste sia per la versione 1.1 che 2.0 del framework), possiamo vedere quanto sia più pesante da eseguire il primo codice rispetto al secondo. Di seguito un report:

      Concatenamento StringBuilder
    Allocated bytes:
    Relocated bytes:
    Final Heap bytes:
    Objects finalized:
    Critical objects finalized:
    Gen 0 collections:
    Gen 1 collections:
    Gen 2 collections:
    Induced collections:
    Gen 0 Heap bytes:
    Gen 1 Heap bytes:
    Gen 2 Heap bytes:
    Large Object Heap bytes:
    Handles created:
    Handles destroyed:
    Handles surviving:
    Heap Dumps:
    Comments:

    379.311.360
    20.536.640
    1.575.328
    0
    0
    449
    47
    0
    0
    847.516
    390.678
    12
    8.784
    27
    0
    27
    0
    0

    544.756
    0
    544.756
    0
    0
    0
    0
    0
    0
    Unknown
    Unknown
    Unknown
    Unknown
    27
    0
    27
    0
    0

    Come è evidente, tanto la memoria allocata quanto il lavoro fatto dal Garbage Collector sono notevolmente ridotti dallo StringBuilder.
    Personalmente, se devo concatenare più di 3 stringhe, uso il secondo codice. Fino a 3, non c'è quasi differenza tra i 2 sistemi.

    Posted Jun 05 2006, 01:17 AM by VitoA with 1 comment(s)
    Filed under:
  • Verificare validità numerica di una stringa

    Autore: Giuseppe Russo (Croghen)

    A volte abbiamo la necessità di verificare che un valore sia numerico o meno. Alcune possibilità in C# sono:
    1) Usare try/catch (non performante e poco elegante)
    2) Referenziare Microsoft.VisualBasic.dll, usare quindi IsNumeric (lo farebbe mai uno sviluppatore C# ???)
    3) Riscriversi IsNumeric

    Ecco qui una semplicissima funzione StringIsNumeric che verifica, appunto, che una stringa sia numerica

    public static bool StringIsNumeric(string StringToAnalyze)
    {
        bool bIsNum = true;
        foreach(char cChar in StringToAnalyze.ToCharArray())
        {
            if(cChar < 48 || cChar > 57)
            {
                bIsNum = false;
            }
        }
        return bIsNum;
    }

    Posted Jun 01 2006, 05:42 AM by VitoA with 5 comment(s)
    Filed under:
  • Verificare la presenza di una connessione di rete attiva

    Autore: Michele Locuratolo

    In applicazioni wondows forms, può esserci l'esigenza di verificare la presenza o meno di una connessione di rete attiva. Con il framework 1.1, l'unica soluzione era quella di richiamare, via PInvoke, la Wininet.dll . Nel framework 2.0 la cosa si è molto semplificata grazie al namespace System.Net.NetworkInformation.NetworkInterface che espone un metodo GetIsNetworkAvailable(); che ritorna un bool.

    Questo ci permette, ad esempio usando un timer, di controllare periodicamente lo stato della connessione e di modificare il comportamento del software di conseguenza. Nell'esempio proposto, cambieremo solo una icona nella status bar.
    Il codice è il seguente:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;

    namespace WindowsApplication2 {
         public partial class Form1 : Form {
             
    bool _NetworkAvailable = false;
                  
    public Form1() {
                        InitializeComponent();
                   }

              private void NetworkTestTimer_Tick(object sender, EventArgs e) {
                   _NetworkAvailable = System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable();
                  
    if (_NetworkAvailable) {
                       
    this.toolStripStatusLabel1.Image = global::WindowsApplication2.Properties.Resources.networkConnections;
                   }
    else {
                       
    this.toolStripStatusLabel1.Image = global::WindowsApplication2.Properties.Resources.none;
                   }
              }
         }
    }

    Il risultato sarà quello mostrato in figura.

    Posted May 30 2006, 05:30 PM by VitoA with 3 comment(s)
    Filed under:
Powered by Community Server (Commercial Edition), by Telligent Systems