Qualche giorno fa ho avuto la necessità di effettuare il join di due DataTable, risultato di interrogazioni su due database differenti, per produrre un unico DataTable da mostrare in un controllo DataGridView. Le due tabelle dovevano essere unite sulla base di un campo ID, secondo l'usuale relazione di chiave-chiave esterna. Mi sono così definito una procedura per eseguire l'operazione:
private void Join(DataTable mainTable, string mainRowField, string parentRowField, DataRelation relation)
{
//Controlla se la tabella contiene la colonna in cui aggiungere i risultati del join.
if (!mainTable.Columns.Contains(mainRowField))
mainTable.Columns.Add(mainRowField);
foreach (DataRow row in mainTable.Rows)
row[mainRowField] = row.GetParentRow(relation)[parentRowField].ToString();
}
Il suo utilizzo è molto semplice. Supponiamo di avere le seguenti DataTable:
DataTable dt1 = new DataTable();
dt1.Columns.Add("NomeUtente");
dt1.Columns.Add("IDCitta");
dt1.Rows.Add(new object[] { "Marco", 12 });
dt1.Rows.Add(new object[] { "Andrea", 12 });
dt1.Rows.Add(new object[] { "Pippo", 13 });
DataTable dt2 = new DataTable();
dt2.Columns.Add("NomeCitta");
dt2.Columns.Add("IDCitta");
dt2.Rows.Add(new object[] { "Taggia", 12 });
dt2.Rows.Add(new object[] { "Sanremo", 13 });
dt2.Rows.Add(new object[] { "Poggibonsi", 14 });
Come è facile intuire, esse dovrebbero essere unite sulla base del valore del campo IDCitta. La prima cosa da fare è inserire queste tabelle in un DataSet e definire una DataRelation tra di esse:
DataSet ds = new DataSet();
ds.Tables.AddRange(new DataTable[] { dt1, dt2 });
ds.Relations.Add("Origine", dt2.Columns["IDCitta"], dt1.Columns["IDCitta"], false);
La relazione è tra la chiave primaria IDCitta di dt2 e la chiave esterna IDCitta in dt1. A questo punto si può finalmente richiamare il metodo Join:
Join(dt1, "Citta", "NomeCitta", ds.Relations[0]);
Dove:
- dt1 è la tabella che si vuole "arricchire" con la giunzione;
- Citta è il nome della colonna di dt1 in cui si vuole aggiungere il risultato della giunzione (se non esiste, verrà creata automaticamente);
- NomeCitta è il campo della tabella in giunzione i cui valori verranno copiati nella colonna Citta della prima tabella;
- ds.Relations[0] è il riferimento alla DataRelation da utilizzare per trovare i record in giunzione.
Tutto qui 
Ormai sono lanciato nella realizzazione di classi di supporto per la gestione della rete. Dopo NetworkChange2, oggi è la volta di una classe che consente di sapere se il PC è collegato a Internet oppure no, nonché di ricevere notifiche se lo stato della connessione cambia:
1 using System;
2 using System.ComponentModel;
3 using System.Threading;
4 using System.Net;
5
6 namespace System.Net.NetworkInformation
7 {
8 public delegate void InternetAvailabilityChangedEventHandler(object sender, InternetAvailabilityEventArgs e);
9
10 public static class NetworkMonitor
11 {
12 private const string HOST = "http://www.microsoft.com";
13
14 private static AsyncOperation operation;
15 private static SendOrPostCallback internetAvailabilityCallback;
16 private static bool internetAvailable;
17
18 private static Timer checkConnection;
19 private const int CHECK_CONNECTION_TIMEOUT = 5000;
20
21 public static event InternetAvailabilityChangedEventHandler InternetAvailabilityChanged;
22
23 static NetworkMonitor()
24 {
25 internetAvailable = false;
26
27 operation = AsyncOperationManager.CreateOperation(null);
28 internetAvailabilityCallback = new SendOrPostCallback(InternetAvailabilityHandler);
29
30 //Attiva il timer che controlla la disponibilità della connessione.
31 checkConnection = new Timer(new TimerCallback(IsConnectionAvailable), null, 1, CHECK_CONNECTION_TIMEOUT);
32 }
33
34 public static bool IsConnectionAvailable()
35 {
36 HttpWebRequest request = null;
37 HttpWebResponse response = null;
38 bool isAvailable = false;
39
40 try
41 {
42 request = (HttpWebRequest)HttpWebRequest.Create(HOST);
43 response = (HttpWebResponse)request.GetResponse();
44
45 if (response.StatusCode == HttpStatusCode.OK)
46 {
47 // HTTP = 200 - Internet connection available, server online
48 isAvailable = true;
49 }
50 }
51 catch (WebException)
52 { }
53 finally
54 {
55 try
56 {
57 if (response != null)
58 response.Close();
59 }
60 catch { }
61 }
62
63 return isAvailable;
64 }
65
66 private static void IsConnectionAvailable(object stateInfo)
67 {
68 checkConnection.Change(Timeout.Infinite, Timeout.Infinite);
69
70 bool isAvailable = IsConnectionAvailable();
71 if (isAvailable != internetAvailable)
72 {
73 internetAvailable = isAvailable;
74 operation.Post(internetAvailabilityCallback, new InternetAvailabilityEventArgs(isAvailable));
75 }
76
77 checkConnection.Change(CHECK_CONNECTION_TIMEOUT, CHECK_CONNECTION_TIMEOUT);
78 }
79
80 private static void InternetAvailabilityHandler(object stateInfo)
81 {
82 if (InternetAvailabilityChanged != null)
83 InternetAvailabilityChanged(null, (InternetAvailabilityEventArgs)stateInfo);
84 }
85 }
86
87 public class InternetAvailabilityEventArgs : EventArgs
88 {
89 private bool isAvailable;
90 public bool IsAvailable
91 {
92 get { return isAvailable; }
93 }
94
95 public InternetAvailabilityEventArgs(bool isAvailable)
96 {
97 this.isAvailable = isAvailable;
98 }
99 }
100 }
In estrema sintesi, questa classe cerca di effettuare una richiesta HTTP (righe 42-43) e, in base al messaggio di risposta, determina se la connessione al Web è disponibile oppure no. Questo non è l'unico metodo possibile, ad esempio un'alternativa si basa sull'utilizzo dell'API di Windows InternetGetConnectedState, ma dove possibile preferisco rimanere nel "mondo managed".
Technorati Tag:
.NET,
Programming,
CS
Finalmente, durante queste vacanze di Natale, ho trovato del tempo da dedicare ad un progetto che mi rimbalzava in testa già da un po'... Ovvero una liberia che fornisse funzionalità UPnP alle applicazioni .NET. In giro avevo già trovato qualcosa del genere, ma niente che rispondesse in pieno alle mie esigenze. In particolare, quello di cui avevo bisogno erano dei metodi per realizzare sistemi di NAT traversal (che dovrebbero essere alla base di un'altra applicazione che sto progettando... Ma questa è un'altra storia
).
E' così nato il progetto SharpUPnP, che nei giorni scorsi ho pubblicato su CodePlex. Al momento è ancora allo stato iniziale: ho definito la procedura per identificare le periferiche UPnP compatibili col NAT presenti nella rete e il metodo per creare un port mapping tra una porta "pubblica" del router e quella di un client all'interno della rete. Nelle mie intenzioni, a breve vorrei completare almeno tutta la parte relativa al NAT traversal, quindi aggiungere anche le funzioni per recuperare l'indirizzo IP pubblico del router, rimuovere un port mapping e ottenere la lista dei mapping attivi.
Come ormai è tradizione, concludo il mio post invitando chiunque sia interessato al progetto a contattarmi
.