自製 LINQ DataAdapter

LINQ 分成兩部份, 第一部份是查詢語法(Query Language), 第二部份是物件資料對映(OR Mapping), 相對於 ADO.NET 的就是 DataReader 與 DataSet, 當然, LINQ 的作法先進很多。

最近試著用 LINQ 在改寫過去用 ASP.NET 2.0 作的網站, 一方面愛死了它的查詢語法, 但另一方面又恨死了它先進的 OR mapping。並不是說 LINQ 的 OR mapping 作的不好, 而是我這是個舊網站, 從 ASP.NET 1.0 開始, 一路昇級到 1.1 與 2.0, 所有的資料存取 API 都已經用 DataSet 或 DataTable 當回傳值, 這幾乎是 ADO.NET 的標準寫法, DataSet 不僅是離線資料表, 也可以直接做資料繫結。DataSet 最大的缺點是結構太複雜, 比起 OR Mapping, DataSet 佔用較多的系統資源。

剛開始改寫 LINQ 時, 查詢語法的部份並不難改, 因為所有的資料存取的程式早已集中在特定的物件裡, 困難的是傳回值的部份, 如果要把 DataSet 改成 OR Mapping, 連帶地所有引用資料存取的程式, 含資料繫結的部份, 全部都要重新寫過, 這個工程就相當大了。我自己寫程式的習慣, 是一次只動一小部份, 然後充分測試之後, 將異動過的程式簽入 Version Control 之後, 再進行下一部份。這種一次要動到很多地方的寫法, 測試起來就難免會有百密一疏。

這時我需要一個過渡的作法, 讓 LINQ 與 DataSet 並存的方法, 才能進一步縮小每次異動的複雜度。LINQ to DataSet 表面上看起來是一個可能的方案, 仔細端詳之後, 發現它只是用 LINQ 的語法去查已存的 DataSet, 這個方案不是我要的, 因為我通常直接拿 DataSet 作資料繫結, 而不會對 DataSet 查詢。我需要的是用 LINQ 語法產生出 DataSet, 以暫時維持現有程式與 API 之間的相容。用 LINQ 產生 DataSet, 表面上不是一個好主意, 但是在程式的過渡時期, 卻能幫我維持程式的穩定, 縮小每次程式異動的測試範圍, 再逐步地汰換 DataSet。

講了這麼多, 是因為 LINQ 根本沒有這個功能, 可能是 LINQ 設計小組覺得 OR Mappint 才是王道, 不相信有人會用 LINQ 來產生 DataSet。還好, 要自己寫一個 LinqDataAdapter 來擔任這個工作並不難:

using System;
using System.Data;
using System.Collections;
using System.Reflection;

namespace Data {
    public class LinqDataAdapter
    {
        private IEnumerable selectCommand = null;
        public IEnumerable SelectCommand {
            set {
                selectCommand = value;
            }
        }

        public int Fill(DataTable table) {
            table.Rows.Clear();
            table.Columns.Clear();

            int n = 0;
            foreach (var r in selectCommand) {
                if (n == 0)
                    foreach (PropertyInfo pInfo in r.GetType().GetProperties()) {
                        if (pInfo.CanRead)
                            table.Columns.Add(pInfo.Name, typeof(Object));
                    }
                DataRow row = table.NewRow();
                foreach (PropertyInfo pInfo in r.GetType().GetProperties()) {
                    if (pInfo.CanRead) { 
                            if ((row[pInfo.Name] = pInfo.GetValue(r, null)) == null)
                                    row[pInfo.Name] = DBNull.Value;

                     }
                }
                table.Rows.Add(row);
                n++;
            }
            return n;
        }
    }
}

 

註: 這個 LinqDataAdatper 目前只在我工作的專案中測試過, 不一定可以適用於所有的狀況, 例如: 只有 SelectCommand, 因為在 ASP.NET 中, 其他的 Command 根本用不到。如果有任何建議, 歡迎與我連絡。