Después de muchos días trabajando en esto, he logrado resolver este problema yo mismo. Lo que hice fue lo siguiente;
En mi clase secundaria original, MemberCode y ElementCode crean una clave única que básicamente indica cuál era el nombre de la columna. Así que llevé esto un paso más allá y agregué un "Nombre_Columna" para tener
public class Child
{
public int MemberCode { get; set; } //Composite key
public int ElementCode { get; set; } //Composite key
public string Column_Name { get; set; } //Unique. Alternative Key
public Object Data { get; set; }
}
Obviamente, esto también se reflejó en la tabla de mi base de datos.
Mi SQL para extraer los datos se veía así;
select p.UniqueID, p.LoadTime, p.reference, c.MemberCode, c.ElementCode , c.column_name, c.Data
from parent as p, child as c
where p.UniqueID = c.UniqueID
//aditional filter criteria
ORDER BY p.UniqueID, MemberCode, ElementCode
ordenar primero por UniqueID es fundamental para garantizar que los registros estén en el orden correcto para su posterior procesamiento.
Yo usaría un dynamic
y un ExpandoObject()
para almacenar los datos.
Así que itero sobre el resultado para convertir el resultado de sql en esta estructura de la siguiente manera:
List<dynamic> allRecords = new List<dynamic>(); //A list of all my records
List<dynamic> singleRecord = null; //A list representing just a single record
bool first = true; //Needed for execution of the first iteration only
int lastId = 0; //id of the last unique record
foreach (DataRow row in args.GetDataSet.Tables[0].Rows)
{
int newID = Convert.ToInt32(row["UniqueID"]); //get the current record unique id
if (newID != lastId) //If new record then get header/parent information
{
if (!first)
allRecords.Add(singleRecord); //store the last record
else
first = false;
//new object
singleRecord = new List<dynamic>();
//get parent information and store it
dynamic head = new ExpandoObject();
head.Column_name = "UniqueID";
head.UDS_Data = row["UniqueID"].ToString();
singleRecord.Add(head);
head = new ExpandoObject();
head.Column_name = "LoadTime";
head.UDS_Data = row["LoadTime"].ToString();
singleRecord.Add(head);
head = new ExpandoObject();
head.Column_name = "reference";
head.UDS_Data = row["reference"].ToString();
singleRecord.Add(head);
}
//get child information and store it. One row at a time
dynamic record = new ExpandoObject();
record.Column_name = row["column_name"].ToString();
record.UDS_Data = row["data"].ToString();
singleRecord.Add(record);
lastId = newID; //store the last id
}
allRecords.Add(singleRecord); //stores the last complete record
Entonces tengo mi información almacenada dinámicamente de la manera plana que requiero.
Ahora el siguiente problema era el ObjectListView que quería usar. Esto no podría aceptar tales tipos dinámicos.
Así que tenía la información almacenada dentro de mi código como quería, pero aún no podía mostrarla como se requería.
La solución fue usar una variante de ObjectListView
conocido como DataListView
. Este es efectivamente el mismo control, pero puede vincularse a datos. Otra alternativa también sería usar un DatagridView, pero quería ceñirme a ObjectListView por otras razones.
Así que ahora tenía que convertir mis datos dinámicos en una fuente de datos. Esto lo hice de la siguiente manera;
DataTable dt = new DataTable();
foreach (dynamic record in allRecords)
{
DataRow dr = dt.NewRow();
foreach (dynamic item in record)
{
var prop = (IDictionary<String, Object>)item;
if (!dt.Columns.Contains(prop["Column_name"].ToString()))
{
dt.Columns.Add(new DataColumn(prop["Column_name"].ToString()));
}
dr[prop["Column_name"].ToString()] = prop["UDS_Data"];
}
dt.Rows.Add(dr);
}
Luego, simplemente asigno mi fuente de datos a DataListView, genero las columnas y listo, ahora tengo mis datos dinámicos extraídos, aplanados y mostrados como los necesito.