Si può prima creare la tabella di destinazione, aggiungere è colonne (sommando il numero di colonne in tutte le tabelle di input) e poi aggiungere è righe concatenando le singole matrici di valori per ogni riga nelle tabelle di input.
Ovviamente, le righe nel DataTable
risultante conterranno i valori come appaiono nella direzione dall'alto verso il basso per ciascuna tabella di input (allineati in alto). Significa anche che il numero di righe risultanti è il numero di righe nella tabella di input più grande.
Per prima cosa inizializziamo e compilare una variabile List<DataTable>
, poi ci effettuare il join, utilizzando questa variabile come un parametro di metodo:
#region table collection initialization
List<DataTable> dts = new List<DataTable>();
var dt = new DataTable();
dt.Columns.Add("Test0", typeof(string));
dt.Rows.Add(1);
dt.Rows.Add(2);
dts.Add(dt);
dt = new DataTable();
dt.Columns.Add("Test1", typeof(int));
dt.Rows.Add(2);
dts.Add(dt);
dt = new DataTable();
dt.Columns.Add("Test3", typeof(int));
dt.Columns.Add("Test4");
dt.Columns.Add("Test5", typeof(int));
dt.Rows.Add(3, "a", 1);
dt.Rows.Add(4);
dt.Rows.Add(5, "a");
dt.Rows.Add(null, "a");
dts.Add(dt);
dt = new DataTable();
dt.Columns.Add("Test6", typeof(DateTime));
dt.Columns.Add("Test7", typeof(int));
dt.Rows.Add(DateTime.Now);
dts.Add(dt);
#endregion
// sample method usage
var result = GetJoinedTable(dts);
Creiamo il metodo GetJoinedTable
che restituirà nella variabile result
tabella unita risultante:
private DataTable GetJoinedTable(List<DataTable> dts)
{
var dest = new DataTable();
//will be used if you have non-unique column names
int counter = 0;
foreach (var column in dts
.SelectMany<DataTable, DataColumn>(t =>
t.Columns.Cast<DataColumn>()))
{
dest.Columns.Add(column.ColumnName, column.DataType);
// if you have non-unique column names use the following instead
//dest.Columns.Add(String.Format("column_{0}", counter++),
// column.DataType);
}
List<object> rowItems;
for (int i = 0; i < dts.Max(t => t.Rows.Count); i++)
{
rowItems = new List<object>();
for (int j = 0; j < dts.Count; j++)
{
if (dts[j].Rows.Count > i)
{
var r = dts[j].Rows[i].ItemArray
.Select((v, index) =>
(v == null || v == System.DBNull.Value)
? GetDefault(dts[j].Columns[index].DataType) : v);
rowItems.AddRange(r);
}
else
{
for (int c = 0; c < dts[j].Columns.Count; c++)
{
rowItems.Add(GetDefault(dts[j].Columns[c].DataType));
}
}
}
dest.Rows.Add(rowItems.ToArray());
}
return dest;
}
Sono necessari per aggiungere il seguente metodo, che restituisce il valore della colonna di default del caso, in base alla DataType
di proprietà della colonna:
object GetDefault(Type t)
{
if (t.IsValueType)
{
return Activator.CreateInstance(t);
}
else
{
return null;
}
}
Perché non limitarsi a scorrere le righe nella tabella di origine e per ogni riga, aggiungere le colonne appropriate alla tabella di destinazione appropriata? Le righe e le colonne (in ogni riga) possono essere ripetute. Sarebbe banale creare una mappa di funzioni da SourceTable/row-> TargetTable/row. – user2246674