Technology Experience
.NET World

Qualche ragionamento su System.Reflection ed Interop con Outlook

In queste sere mi sto divertendo, se così si può dire, con la creazione di un plug-in per Microsoft Outlook 2007. Lo scopo è quello di importare nel folder Contacts di Outlook un’archivio di anagrafica, suddiviso in diverse categorie. Per farla breve vi faccio vedere solo due tabelle che illustrano il contenuto dell’anagrafica di cui sto parlando.

La tabella Tipi contiene la definizione delle categorie. Il campo “Tipo” contiene il nome della categoria (Cantanti, Gruppi, Discoteche, Pub, Feste in Piazza, etc.). Il campo “Lettera” contiene un singolo carattere (C, G, D, P, F, etc.) che indica con quale lettera è identificata una certa categoria; tale campo serve per andare in JOIN sulla tabella Anagrafica. In questo modo ogni record di Anagrafica appartiene ad una ed una sola categoria della tabella Tipi. Le relazioni mostrate nell’immagine qui sopra credo siano abbastanza chiare. Non accetto critiche sul database: l’ho ereditato io 10 anni fa su Access 2.0 e oggi gira in produzione su Access 2003 dopo tutti questi anni. Mi dovrei vergognare per questo? Forse sì, forse no, ma così è e così me lo prendo. 🙂

Comunque, lo scopo è quello di elaborare ogni record di Anagrafica e di creare un’istanza di Microsoft.Office.Interop.Outlook.ContactItem, popolandone tutte le proprietà pubbliche, e poi di salvarlo dentro Outlook 2007. Ecco uno stralcio di C# per dare l’idea:

1 foreach (MyDataSet.AnagraficaRow row in dataset.Anagrafica.Rows) 2 { 3 if (ContactAdding != null) ContactAdding(this, new ContactEventArgs(string.Empty)); 4 Outlook.ContactItem item = ContactServices.CreateContact(row); 5 6 if (item != null) 7 { 8 item.Save(); 9 if (ContactAdded != null) ContactAdded(this, new ContactEventArgs(item.FullName)); 10 } 11 }

Dato che si tratta di un prototipo mi sono semplificato la vita con un DataSet tipizzato. Ciclo su tutte le righe presenti nella DataTable. Per ogni riga sollevo due eventi ContactAdding e ContactAdded (ContactAdding e ContactAdded sono due public event, ContactEventArgs è una classe derivata da EventArgs). Ma il succo è la chiamata al metodo statico CreateContact, metodo che prende in input un’istanza di AnagraficaRow e restituisce un’istanza di ContactItem.

Tecnicamente parlando, possiamo implementare questo metodo in tanti modi diversi. Io ne ho pensati ed implementati due:

  1. assegno una ad una le proprietà pubbliche di ContactItem valorizzandole con i campi esposti da AnagraficaRow
  2. uso Reflection per fare qualcosa di più ingegnoso e gestibile nel prossimo futuro

Per il punto (1) il codice dovrebbe essere una cosa simile a questa:

1 ContactItem item = (ContactItem)folder.Items.Add(OlItemType.olContactItem); 2 if (!row.IsDenominazioneNull()) item.FullName = row.Denominazione; 3 if (!row.IsTitolareNull()) item.ManagerName = row.Titolare; 4 ... 5 ...

Alla riga (1) creo un’istanza di ContactItem, poi valorizzo le proprietà FullName, ManagerName e così via, per le decine di campi di cui necessitate (le mie sono 18). Noioso e davvero poco manutenibile.

Il punto 2 è più interessante, perchè tramite Reflection possiamo creare dinamicamente un’istanza di ContactItem. Creiamo una classe ContactFieldImport così implementata:

   1: public class ContactFieldImport
   2: {
   3:     public string Property { get; private set; }
   4:     public string Field { get; private set; }
   5:  
   6:     public ContactFieldImport(string property, string field)
   7:     {
   8:         Property = property;
   9:         Field = field;
  10:     }
  11: }

Ogni istanza di ContactFieldImport rappresenta una coppia di Property/Field. Possiamo così definire una List<ContactFieldImport>:

   1: List<ContactFieldImport> props = new List<ContactFieldImport>();
   2: props.Add(new ContactFieldImport("FullName", "Denominazione"));
   3: props.Add(new ContactFieldImport("ManagerName", "Titolare"));
   4: props.Add(new ContactFieldImport("HomeAddress", "Indirizzo"));
   5: props.Add(new ContactFieldImport("HomeAddressPostalCode", "CAP"));

Ogni coppia definisce come popolare un oggetto ContactItem. Prendo il valore del campo (Field) e con esso valorizzo la proprietà pubblica (Property) di ContactItem. A questo punto il gioco è semplice.

   1: foreach (ContactFieldImport cfi in props)
   2: {
   3:     PropertyInfo pi = typeof(_ContactItem).GetProperty(cfi.Property);
   4:  
   5:     if (pi != null && pi.CanWrite && !string.IsNullOrEmpty(row[cfi.Field].ToString()))
   6:     {
   7:         pi.SetValue(item, row[cfi.Field], null);
   8:     }
   9: }

Itero su tutti gli elementi contenuti nella lista props. Per ciascuno degli elementi, ottengo un riferimento alla PropertyInfo puntata da cfi.Property. Se la PropertyInfo è valida, può essere scritta (CanWrite == true) e se l’AnagraficaRow corrente è una stringa valida, allora imposto la proprietà dell’istanza di ContactItem (riga 7).

Dal punto di vista prestazionale, il ciclo for…each qui sopra è molto più veloce rispetto al primo metodo, quello che cioè che settava le proprietà pubbliche di ContactItem una ad una, senza una sorta di automatismo. Non so darvi con esattezza l’ordine di grandezza, ma ho provato ad importare con tutti e due i metodi qualcosa come 1.400 anagrafiche e tramite Reflection le cose erano molto migliori (< 1 minuto).

Il risultato finale visto in Outlook 2007 è il seguente:

Dentro Contatti ho creato un folder Winshow Planning (nome del progetto), all’interno del quale ho creato tre subfolders (Agenti, Artisti e Luoghi). In ciascuno dei tre ho finalmente creato tanti ulteriori subfolders (presi dal database). Ogni folder contiene i contatti assegnati ad ogni categoria.

‘Sto post è davvero troppo lungo e prolisso. Se avete domande, sono qua a disposizione. 😉

Send to Kindle

Igor Damiani

La sua passione per l'informatica nasce nella prima metà degli anni '80, quando suo padre acquistò un Texas Instruments TI-99. Da allora ha continuato a seguire l'evoluzione sia hardware che software avvenuta nel corso degli anni. E' un utente, un videogiocatore ed uno sviluppatore software a tempo pieno. Igor ha lavorato e lavora anche oggi con le più moderne tecnologie Microsoft per lo sviluppo di applicazioni: .NET Framework, XAML, Universal Windows Platform, su diverse piattaforme, tra cui spiccano Windows 10 piattaforme mobile. Numerose sono le app che Igor ha creato e pubblicato sul marketplace sotto il nome VivendoByte, suo personale marchio di fabbrica. Adora mantenere i contatti attraverso Twitter e soprattutto attraverso gli eventi delle community .NET.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Questo sito usa Akismet per ridurre lo spam. Scopri come i tuoi dati vengono elaborati.