Po wielu dniach pracy nad tym udało mi się sam rozwiązać ten problem. To, co zrobiłem, było następujące;
W mojej oryginalnej klasie potomnej MemberCode i ElementCode tworzą unikalny klucz, który zasadniczo określa nazwę kolumny. Poszedłem więc o krok dalej i dodałem „Nazwa_kolumny”, aby mieć
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; }
}
Było to oczywiście odzwierciedlone również w mojej tabeli bazy danych.
Mój SQL do wyodrębnienia danych wyglądał wtedy tak;
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
zamówienie najpierw według UniqueID ma kluczowe znaczenie dla zapewnienia, że rekordy są we właściwej kolejności do późniejszego przetwarzania.
Użyłbym dynamic
i ExpandoObject()
do przechowywania danych.
Więc iteruję wynik, aby przekonwertować wynik sql na tę strukturę w następujący sposób;
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
Następnie moje informacje są przechowywane dynamicznie w płaski sposób, którego wymagałem.
Teraz następnym problemem był ObjectListView, którego chciałem użyć. To nie mogło zaakceptować takich dynamicznych typów.
Miałem więc informacje przechowywane w moim kodzie tak, jak chciałem, ale nadal nie mogłem ich wyświetlić zgodnie z wymaganiami.
Rozwiązaniem było użycie wariantu ObjectListView
znany jako DataListView
. Jest to w rzeczywistości ta sama kontrola, ale może być powiązana z danymi. Inną alternatywą byłoby również użycie DatagridView, ale chciałem trzymać się ObjectListView z innych powodów.
Więc teraz musiałem przekonwertować moje dynamiczne dane na źródło danych. Zrobiłem to w następujący sposób;
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);
}
Następnie po prostu przypisuję moje źródło danych do DataListView, generuję kolumny i hej presto, mam teraz wyodrębnione, spłaszczone i wyświetlone tak jak potrzebuję moje dane dynamiczne.