CODE-將LINQ查詢結果轉成ADO.NET DataTable
8 | 26,092 |
跟同事討論在新專案中開始採用LINQ,但有些古老元件的API只接受DataTable參數,我想到以前想過可以利用Reflection將LINQ查詢結果轉成DataTable,花了點時間,參考網路上的文章,整理成以下範例:
排版顯示純文字
using System;
using System.Collections.Generic;
using System.Linq;
using System.Data;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
//REF: http://3.ly/y8rA
//註: .NET 3.5有個IEnumerable.CopyToDataTable<T>(),但T只接受DataRow(感覺有點莊孝維)
static DataTable LinqQueryToDataTable<T>(IEnumerable<T> query)
{
DataTable tbl = new DataTable();
PropertyInfo[] props = null;
foreach (T item in query)
{
if (props == null) //尚未初始化
{
Type t = item.GetType();
props = t.GetProperties();
foreach (PropertyInfo pi in props)
{
Type colType = pi.PropertyType;
//針對Nullable<>特別處理
if (colType.IsGenericType
&& colType.GetGenericTypeDefinition() == typeof(Nullable<>))
colType = colType.GetGenericArguments()[0];
//建立欄位
tbl.Columns.Add(pi.Name, colType);
}
}
DataRow row = tbl.NewRow();
foreach (PropertyInfo pi in props)
row[pi.Name] = pi.GetValue(item, null) ?? DBNull.Value;
tbl.Rows.Add(row);
}
return tbl;
}
class Person
{
public string Name { get; set; }
public int Score { get; set; }
public DateTime RegDate { get; set; }
}
static void Main(string[] args)
{
List<Person> people = new List<Person>() {
new Person { Name="Jeffrey", Score=32767, RegDate = DateTime.Today },
new Person { Name="Darkthread", Score=65535, RegDate = DateTime.Now }
};
var q = from p in people
where p.Score > 255
select p;
DataTable t = LinqQueryToDataTable(q);
Console.WriteLine("Row Count={0}", t.Rows.Count);
DataRow r = t.Rows[0];
Console.WriteLine("1st Person Name={0} Score={1} RegDate={2}",
r["Name"], r["Score"], r["RegDate"]);
Console.Read();
}
}
}
Comments
# by LEE
請問黑暗大大一些問題 1. DataTable 和 NEW DataTable 到底有什麼不同? 關於有沒有NEW 的宣告友什麼差異,我在google搜尋了很久,也看過很多文章,可是我還是不太清楚 2.DataTable 和DataSet 需不需要人工 .Dispose()? 因為網路上很多篇文章都有說,NET Framework自己有清理機制,可是也有看到一些人說避免一些影響(EX:佔用太多記憶體) ,所以當DataTable等不用時就因該.Dispose。 到底什麼樣的說法才是正確的, 請黑暗大大指點迷津,謝謝您了。
# by LEE
不好意思再請問黑暗大大一個問題 依據您的經驗有哪些東西是用完一定要.Dispose或是要寫成using的呢?
# by Jeffrey
to LEE, 1.不太懂你的問題,所以我用猜的。你是否想問DataTable tbl = new DataTable(); 裡前後兩次出現的DataTable有何不同? 第一個DataTable宣告了變數tbl的型別,而new DataTable()產生了一個DataTable的Instance設定到tbl變數上。這些算是基本的C#語言基礎,靠Google不易學得完整,建議可以找一本紙本書籍入門。 2.Dispose主要用在有引用非.NET資源(術語叫Unmanaged Resouce)的物件上,例如HD檔案、網路連線、資料庫連線這些資源,都要在不用時明確釋放,無法靠GC(即你所謂的.NET清理機制)處理。DataTable算是純的.NET物件,習慣上不用Dispose()。 3.當有需要Dispose的場合,寫成using比較省事。參考文章: http://bit.ly/d9wlSI
# by LEE
黑暗大大 非常感謝你這麼快速的解答 1.主要是因為我看網頁上有看到NET.VB 宣告時有人寫 Dim dtTable as dataTable 有時候是寫 Dim dtTable as new dataTable 所以不清楚這樣寫的差別 2.HD檔案 是指什麼呢?是指使用硬碟上的檔案嗎? EX: My.Computer.FileSystem.ReadAllText 這類嗎?
# by Jeffrey
to LEE, 1.嘿,猜錯了。原來是VB.NET,Dim dtTable as New DataTable表示宣告變數時順便新增一個Instance指派給變數,而Dim dtTable as DataTable若未經指派,dtTable等於Nothing 2.using (StreamReader sr = new StreamReader("C:\\Temp\\Test.txt")) <-- 像這樣開啟Harddisk的檔案就建議用using 3.給錯URL了,糗。是這個: http://bit.ly/d9wlSI
# by Edward
黑大,請教您一個跟本篇教無關的問題。 我做一個查詢頁面,可以讓使用者輸入條件,然後找出符合的結果,今天使用者要求,想要在第一次查詢的結果中,再輸入不同的條件做第二次查詢,但第二次查詢的標的是第一次查詢的結果資料集。不知有沒有什麼比較好的設計方法,謝謝
# by Jeffrey
to Edward, 我會選擇把第一次查詢的條件保留下來,第二查詢時用LINQ串接兩次查詢條件。例如:第一次查Group="a",結果為 var qry = ctx.TheTable.Where(o => o.Group == "a"); return qry.ToList(); 第二次再追加Price < 100,就等於 var qry = ctx.TheTable.Where(o => o.Group == "a"); qry = qry.Where(o => o.Price < 100); return qry.ToList();
# by Edward
嗯嗯,這的確是個好方法,謝謝您