探討IEnumable(T)扁平化與轉換成DataTable
爾偶會有一些需求是要將ORM物件包轉換成DataTable的作法,其實.NET Framework在System.Data.DataSetExtensions有提供一個CopyToDataTable,但是因為太難用了乾脆自己寫一個比較快。話不多說直接看程式碼。
以下的兩段程式碼,我都是寫成擴充類別,只要是IEnumable(T)都可以支援使用。
將IEnumable(T)轉換成DataTable
這一段程式碼我考慮的比較多,包括把一些簡單的List(T)都考慮進來了,所以程式碼會比較冗長,另外包括ORM常遇到的Nullable也有被考慮在其中。
public static System.Data.DataTable ToDataTable<T>(this System.Collections.Generic.IEnumerable<T> oData, string cTemp = "未命名")
{
System.Data.DataTable oDT = new System.Data.DataTable() { TableName = cTemp };
//如果沒有任何資料就回傳空表格
if (oData == null || !oData.Any()) { return oDT; }
//判斷是否為單型別資料,例如:List<string>
bool bIsSingleColumn = typeof(T).IsPrimitive || typeof(T) == typeof(System.String);
//依照不同的資料型態進行不同的拆包
switch (bIsSingleColumn)
{ //一半IEnumerable資料包
case false:
//將資料表反射屬性回來
System.Reflection.PropertyInfo[] oPIs = typeof(T).GetProperties();
//如果屬性裡面具有陣列型態的次資料,那就把它移除(DataTable只可以表示兩個維度的資料)
oPIs = oPIs.Where(p => !p.PropertyType.IsArray).ToArray();
//將物件屬性以陣列的方式插入到欄位
oDT.Columns.AddRange(oPIs.Select(p => new System.Data.DataColumn(p.Name, System.Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType)).ToArray());
//資料填充
System.Collections.ArrayList oAL;
for (int i = 0; i < oData.Count(); i++)
{
oAL = new System.Collections.ArrayList();
foreach (System.Reflection.PropertyInfo oPI in oPIs)
{ //如果是NULL的話就插入DBNull.Value
oAL.Add(oPI.GetValue(oData.ElementAt(i), null) ?? System.DBNull.Value);
}
oDT.Rows.Add(oAL.ToArray());
}
break;
//簡單單列資料包
default:
//因為不具備屬性,因此依照原先資料型態插入格式
oDT.Columns.AddRange(new System.Data.DataColumn[] { new System.Data.DataColumn("Data List", typeof(T)) });
//直接把所有的資料插入
for (int i = 0; i < oData.Count(); i++)
{ oDT.Rows.Add(oData.ElementAt(i)); }
break;
}
//回傳DataTable結果
return oDT;
}
將多維度的IEnumable(T)扁平化成Key、Value
這段程式碼算是比較單純的,引用了強大的JSON.NET來幫我們扁平化,然後再逐一的新增到Dictionary裡面。
public static System.Collections.Generic.Dictionary<string, string> ToFlatten<T>(this System.Collections.Generic.IEnumerable<T> oData)
{
var oTokens = Newtonsoft.Json.Linq.JArray.FromObject(oData).Descendants().Where(p => p.Count() == 0);
return oTokens.Aggregate(
new System.Collections.Generic.Dictionary<string, string>(),
(oList, oToken) =>
{
oList.Add(oToken.Path, oToken.ToString());
return oList;
}
);
}
接下來是測試時間
輸出DataTable的程式碼我就統一寫在這裡了,等一下就不顯示在程式碼裡面。
//印出DataTable
foreach (System.Data.DataRow oRow in oDT.Rows)
{
foreach (System.Data.DataColumn oCol in oDT.Columns)
{ Write($"{oRow[oCol]}, "); }
WriteLine();
}
簡單的List(string)範例:
var oData = new System.Collections.Generic.List<string>()
{
"AAA111",
"BBB222"
};
System.Data.DataTable oDT = oData.ToDataTable();
結果:
AAA111,
BBB222,
進階的List(ORM)範例:
class MyORM
{
public string cName { get; set; }
public int iMoney { get; set; }
}
var oData = new System.Collections.Generic.List<MyORM>()
{
new MyORM(){ cName="小明", iMoney=1234 },
new MyORM(){ cName="小華", iMoney=5678 },
};
System.Data.DataTable oDT = oData.ToDataTable();
結果:
小明, 1234,
小華, 5678,
匿名型別且多維度範例:
var oData = new[]
{
new
{
cName = "小明",
iMoney = 1234,
cFamily = new[]
{
new
{
cRoot = "小明爸爸",
cGroup = new[]
{
new { cName = "AAA", cRelation = "母子" },
new { cName = "BBB", cRelation = "姊弟" },
new { cName = "CCC", cRelation = "兄弟" },
},
}
}
},
new
{
cName = "小華",
iMoney = 5678,
cFamily = new[]
{
new
{
cRoot = "小華媽媽",
cGroup = new[]
{
new { cName = "DDD", cRelation = "姊妹" },
new { cName = "EEE", cRelation = "兄妹" },
new { cName = "FFF", cRelation = "大姑" },
new { cName = "GGG", cRelation = "小姑" }
},
}
}
}
};
System.Data.DataTable oDT = oData.ToFlatten().ToDataTable();
這種應用特別適用輸出在EXCEL檔案裏面,當你的資料關聯的維度很高時,客戶如果要求無論如何都要在EXCEL裡面顯示(但EXCEL只有兩個維度),其實這個擴充類別是一個很好的解決方案,一股腦丟給你,喜歡看自己去慢慢看吧。
[0].cName, 小明,
[0].iMoney, 1234,
[0].cFamily[0].cRoot, 小明爸爸,
[0].cFamily[0].cGroup[0].cName, AAA,
[0].cFamily[0].cGroup[0].cRelation, 母子,
[0].cFamily[0].cGroup[1].cName, BBB,
[0].cFamily[0].cGroup[1].cRelation, 姊弟,
[0].cFamily[0].cGroup[2].cName, CCC,
[0].cFamily[0].cGroup[2].cRelation, 兄弟,
[1].cName, 小華,
[1].iMoney, 5678,
[1].cFamily[0].cRoot, 小華媽媽,
[1].cFamily[0].cGroup[0].cName, DDD,
[1].cFamily[0].cGroup[0].cRelation, 姊妹,
[1].cFamily[0].cGroup[1].cName, EEE,
[1].cFamily[0].cGroup[1].cRelation, 兄妹,
[1].cFamily[0].cGroup[2].cName, FFF,
[1].cFamily[0].cGroup[2].cRelation, 大姑,
[1].cFamily[0].cGroup[3].cName, GGG,
[1].cFamily[0].cGroup[3].cRelation, 小姑,