手動打造強型(typed)的 DataTable/DataRow

前言

最近正在用 C# 寫一個小視窗軟體 , 裡面會有個功能就是讀取 XML 及寫入 XML , 但我懶 , 想說用 DataSet 來做就好了 , 但後面改了很多次 , 覺得 DataRow 的資料沒有型別 , 後面比較難維護 , 所以去 Google 搜尋看看如何做強型的 DataRow , 當然我找到了 , 不然不會寫這篇 , 這篇是個筆記 , 怕那個網址掛了以後沒得看了 , 原文網址在 : http://www.codeproject.com/KB/database/TypedDataTable.aspx?display=Print

本篇不是要翻譯原文 , 只是我自己整理的筆記自己看得懂就好了 , 有興趣的人直接看原文說明比較詳盡 , 這裡就用一個簡單的讀取及寫入 xml 檔案來做範例

範例說明

假設有一個 XML 檔案格式如下

<?xml version="1.0" encoding="utf-8" ?>
<tips>
<item><subject>Subject 1</subject><description>Some Text1 ...</description></item>
<item><subject>Subject 2</subject><description>Some Text2 ...</description></item>
</tips>

用 DataSet 其實就可以讀取這種格式並且更新內容後寫回 XML 檔案 , 但我們現在要用強型的 DataTable 做

範例程式

    class MyRow : DataRow
    {
        /// <summary>
        /// 讀取及設定標題
        /// </summary>
        public string subject
        {
            get { return (string)base["subject"]; }
            set { base["subject"] = value; }
        }
        /// <summary>
        /// 讀取及設定內容
        /// </summary>
        public string description
        {
            get { return (string)base["description"]; }
            set { base["description"] = value; }
        }


        internal MyRow(DataRowBuilder builder)
            : base(builder)
        {
            // 設定預設屬性
            columnSubject = string.Empty;
            columnDescription = string.Empty;
        }
    }

    class MyTable : DataTable
    {

        public MyTable() : base("item")
        {
            Columns.Add(new DataColumn("subject", typeof(string)));
            Columns.Add(new DataColumn("description", typeof(string)));
        }

        public void Add(MyRow row)
        {
            Rows.Add(row);
        }

        public void Remove(MyRow row)
        {
            Rows.Remove(row);
        }

        protected override Type GetRowType()
        {
            return typeof(MyRow);
        }

        protected override DataRow NewRowFromBuilder(DataRowBuilder builder)
        {
            return new MyRow(builder);
        }

        // public new MyRow NewRow()
        // {
        //   MyRow row = (MyRow)NewRow();
        //   return row;
        // }

        public MyRow GetNewRow()
        {
            MyRow row = (MyRow)NewRow();
            return row;
        }
        /// <summary>
        /// 寫這個可以用陣列方式取得所有的 MyRow
        /// </summary>
        /// <param name="idx"></param>
        /// <returns></returns>
        public MyRow this[int idx]
        {
            get { return (MyRow)Rows[idx]; }
        }

    }

MyTable 及 MyRow 這兩個類別分別繼承自 DataTable 及 DataRow , 現在就可以用下面的方式新增一筆 XML 資料

DataSet ds = new DataSet();
MyTable table = new MyTable();
ds.ReadXml("test.xml");
ds.Tables.Add(table);
MyRow row = table.GetNewRow();
row.subject= "subject 3";
row.description = "description 3";
table.Add(row);
ds.WriteXml("test.xml");

稍微講解一下

基本上有一些 method 必須複寫(override) , 行 49 , 54 是必須的 , 這樣才有辦法用 MyTable 去產生 MyRow  的型別 , 而行 33 的 base("item") 是為了 XML 範例做的 , 因為每一行資料都是 <item> , 在 DataTable 的 name 就要指定成 "item" , 所以用 base("item") 呼叫 DataTable 的建構子做

行 21 也是必須的 , 我不明白其道理 , 看 MSDN 只知道這個建構子必須要有 , 我想應該和 MyTable.NewRowFromBuilder() 有關係

而在 MyRow 中我定義了 MyRow.subject 及 MyRow.description 分別對應到 XML 的 <subject> 及 <description> 的欄位 , 在 VS.NET 編輯器中就可以自動跳出類別的屬性使用 , 而且編譯時期也會檢查型別

寫的很簡單 , 我覺得應該看原文會比較清楚整個來龍去脈  , 而且原文也有介紹怎麼定義事件 , 講解完B …

發佈留言