跟同事討論在新專案中開始採用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

嗯嗯,這的確是個好方法,謝謝您

Post a comment