Depois de muitos dias trabalhando nisso, consegui resolver esse problema sozinho. O que eu fiz foi o seguinte;
Na minha classe filha original, o MemberCode e o ElementCode criam uma chave exclusiva que basicamente declara qual era o nome da coluna. Então dei um passo adiante e adicionei um "Column_Name" para que eu tivesse
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, isso também se refletiu na minha tabela de banco de dados.
Meu SQL para extrair os dados ficou assim;
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 primeiro pelo UniqueID é fundamental para garantir que os registros estejam na ordem correta para processamento posterior.
O eu usaria um
dynamic
e um ExpandoObject()
para armazenar os dados. Então eu itero sobre o resultado para converter o resultado sql nesta estrutura da seguinte forma;
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
Então eu tenho minhas informações armazenadas dinamicamente da maneira simples que eu precisava.
Agora, o próximo problema era o ObjectListView que eu queria usar. Isso não pode aceitar esses tipos dinâmicos.
Então, eu tinha as informações armazenadas no meu código como eu queria, mas ainda não consegui exibi-las como era necessário.
A solução foi usar uma variante do
ObjectListView
conhecido como DataListView
. Este é efetivamente o mesmo controle, mas pode ser vinculado a dados. Outra alternativa também seria usar um DatagridView, mas eu queria manter o ObjectListView por outros motivos. Então agora eu tive que converter meus dados dinâmicos em uma fonte de dados. Isso eu fiz da seguinte forma;
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);
}
Então eu simplesmente atribuo minha fonte de dados ao DataListView, gero as colunas, e pronto, agora tenho meus dados dinâmicos extraídos, nivelados e exibidos como eu preciso.