用Dotnet做開發,不少程序員都在為是用DataSet,DataTable,DataRow(以下簡用:DotNet數據對象) 作為項目的數據承載對象還是使用自定義的數據類和自定數據集合而犯難,社區中也有相關話題的不少討 論。前者作為Ado.net標准的數據集對象,本身有非常強大的功能,但也存在不少的問題,如:弱類型, 非面向對象,數據類對象體積相對較大等。所以不少的設計人員選擇了使用了自定義數據類和數據集作為 自己項目的數據承載對象,解決上面的問題的同時也出現了一些其它的問題,比如:數據類編寫起來麻煩 ,花時間且沒有技術含量,降低開發效率,無法享受到Ado.net提供的很多數據操作的便利如(數據綁定 的離線排序,離線查詢,如此等等),如果直接使用Ado.net進行數據訪問的話,還需要寫比較多的數據 訪問層的代碼,大大降低了開發效率。關於自定義數據類的話題,MSDN上有一篇很好的文章。《掌握 ASP.NET 之路:自定義實體類簡介》
本篇就是的主題就是來討論如何更好地設計自定義數據,讓它結合自定義數據對象與DotNet數據對象 無縫結合,發揮各自優點,以利於我們在使用時得心應手。當初考慮這樣做主要是原因是要使用 IBatisNet作為持久層工具,而不管是IBatisNet還是NHibernate都是使用自定義數據類與數據集合,它們 都與DotNet數據對象天生沒有聯系的,如果使用它,而我就無法使用DotNet數據對象了,這樣就有點偏離 DotNet的味道了,而在需要的時候再通過一些方法去轉換得到DotNet數據對象,就顯得麻煩而且復雜得多 。
上面的費話引出了我要解決的問題,那下面就詳細介紹一下如何去設計這個數據類吧。基本原理就是 ,表現用自定義的數據類作為表現形式,而內部存儲數據的部分使用DotNet數據對象作為數據容器。利用 自定義數據類的Property,封裝對DataRow的每一列的讀取。我們可以這麼理解,每一個自定義對象對應 數據庫中的每一行記錄(DataRow),而一個數據集合就對應數據庫的一個表(DataTable)。數據類的定義 就由:
1public class CommonDataObject
2{
3 private int id;
4
5 public int ID
6 {
7 get { return id; }
8 set { id = value; }
9 }
10}
變為類似於:
1public class DataObject
2{
3 private DataRow m_dataRow;
4
5 public int ID
6 {
7 get
8 {
9 if (m_dataRow["ID"] == DBNull.Value)
10 {
11 return 0;
12 }
13 return (int)m_dataRow["ID"];
14 }
15 set
16 {
17 m_dataRow["ID"] = value;
18 }
19 }
20}
上面(代碼2)的數據類就一個雛形,但是還存在一些問題。DataRow從哪裡來的?因為對DataRow來說, 它本身是不能夠實例化的,必須通過DataTable的NewRow方法生成一個與DataTable對象數據(表)結構相 對應的數據行。那麼是不是可以在數據類定義一個DataTable實體,只做為數據一個架構存放在那邊。數 據類的定義修改為(抽象類的定義,作為一個基類,構造數據結構的方法留子類對實現,以便後面的多態 應用):
1public abstract class AbstractDataObject
2{
3 private DataTable m_dataTable;
4 private DataRow m_dataRow;
5
6 public AbstractDataObject()
7 {
8 m_dataTable = BuildSchema();
9 m_dataRow = m_dataTable.NewRow();
10 }
11 protected abstract DataTable BuildSchema();
12
13 public int ID
14 {
15 get
16 {
17 if (m_dataRow["ID"] == DBNull.Value)
18 {
19 return 0;
20 }
21 return (int)m_dataRow["ID"];
22 }
23 set
24 {
25 m_dataRow["ID"] = value;
26 }
27 }
28}
這樣(代碼3)的定義是可行,並且也能工作。但是一眼看就能看出它的問題,那就是在每個數據類的對 象都要生成並且保存有一份DataTable,而作用僅僅是為了實例化一個DataRow,從實際效果上來看,這樣 的代價實在太大了,因為DataTable本身是一個重對象,實例化它,保存它都需要比較大時間和空間上的 損耗。最開始就是這樣設計的,但是當前我從數據庫讀取了30000多條數據的時候,機器已經的內存,CPU 已經到極限了,也就是這樣的設計是不合理的。那是不是可以在數據類裡定義一個公共的數據容器,同一 個數據類生成的對象都使用這個容器,這樣性能上的損耗就可以降到最低。原來生成10000個對象,就要 創建10000個DataTable,而現在就只要生成一個,而且不管在什麼情況下都只要生成一個。
1public class DataObject
2{
3 private static DataTable m_dataTable = BuildSchema();
4 private DataRow m_dataRow;
5
6 public DataObject()
7 {
8 m_dataRow = m_dataTable.NewRow();
9 }
10 protected static DataTable BuildSchema()
11 {
12 DataTable m_dataTable = new DataTable();
13 DataColumn m_dc = new DataColumn("ID", typeof(int));
14 m_dataTable.Columns.Add(m_dc);
15 }
16 public int ID
17 {
18 get
19 {
20 if (m_dataRow["ID"] == DBNull.Value)
21 {
22 return 0;
23 }
24 return (int)m_dataRow["ID"];
25 }
26 set
27 {
28 m_dataRow["ID"] = value;
29 }
30 }
31}
如果單單是定義一個數據對象,這樣(代碼4)的設計完全是可以。但是有一個問題是Datatable的對象 是靜態的,因為每個數據類的結構都是不一樣的,這樣對我就無法實現多態,而多態對於後面要定義的數 據集合是至關重要的,因為數據集合對於每個數據類都是一樣的,只需要定義一個數據集合而不用每個數 據類對應一個數據集合。要實現上面的功能,並且還要實現多態,最終的數據類定義為:
1[Serializable]
2public abstract class DataObjectBase : ISerializable
3{
4 private DataTable m_dataTable;
5 private DataRow m_dataRow;
6
7 /**//// <summary>
8 /// Initializes a new instance of the <see cref="T:DataObjectBase"/> class.
9 /// </summary>
10 public DataObjectBase()
11 {
12 }
13 /**//// <summary>
14 /// </summary>
15 /// <value></value>
16 public System.Data.DataRow ObjectRow
17 {
18 get
19 {
20 return m_dataRow;
21 }
22 set
23 {
24 m_dataRow = value;
25 }
26 }
27 /**//// <summary>
28 /// Initializes a new instance of the <see cref="T:DataObjectBase"/> class.
29 /// <remarks> 反序列化構造函數</remarks>
30 /// </summary>
31 /// <param name="info">The info.</param>
32 /// <param name="context">The context.</param>
33 protected DataObjectBase(SerializationInfo info, StreamingContext context)
34 {
35 DataTable dt = info.GetValue ("DataTable", typeof(DataTable)) as DataTable;
36 this.m_dataRow = dt.Rows [0];
37 }
38 ISerializable Members#region ISerializable Members
39 [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)]
40 public void GetObjectData(SerializationInfo info, StreamingContext context)
41 {
42 DataTable dt = this.m_dataRow.Table.Clone();
43 DataRow row = dt.NewRow();
44 row.ItemArray = m_dataRow.ItemArray;
45 dt.Rows.Add(row);
46 info.AddValue("DataTable", dt, dt.GetType());
47 }
48 #endregion
49
50 public DataTable DataContainer
51 {
52 get { return m_dataTable; }
53 protected set { m_dataTable = value; }
54 }
55}
這是一個數據類的抽象基類,讓它實現ISerializable接口,支持序列化,子類不需做任何事就可以支 持序列化了,至於序列化,對每一個對象的序列,只要序列對應的那個DataRow就行了。(當然了 Serializable屬性是必不可少的。)
1/**//// <summary>
2 /// Initializes a new instance of the <see cref="T:DemoDataObject"/> class.
3 /// </summary>
4 public DemoDataObject()
5 : base()
6 {
7 DataContainer = ObjectSchemaClass.Instance.DataContainer;
8 ObjectRow = DataContainer.NewRow();
9 }
10 /**//// <summary>
11 /// Initializes a new instance of the <see cref="T:DemoDataObject"/> class.
12 /// </summary>
13 /// <param name="p_dataRow">The p_data row.</param>
14 public DemoDataObject(DataRow p_dataRow)
15 {
16 ObjectRow = p_dataRow;
17 }
18
19 /**//// <summary>
20 /// 返回序列化構造函數
21 /// </summary>
22 /// <param name="info">The info.</param>
23 /// <param name="context">The context.</param>
24 protected DemoDataObject (SerializationInfo info, StreamingContext context)
25 : base(info, context)
26 {
27 }
28 /**//// <summary>
29 /// Gets or sets the ID.
30 /// </summary>
31 /// <value>The ID.</value>
32 public int ID
33 {
34 get
35 {
36 if (ObjectRow[STR_Id_FIELD] != null)
37 return (int)ObjectRow[STR_Id_FIELD];
38 return 0;
39 }
40 set { ObjectRow[STR_Id_FIELD] = value; }
41 }
42}
每個數據類裡,內嵌一個私有類,將它定義為單實例的,做為數據容器。
以上的就是一個支持數據類與DotNet數據對象無無縫結合的完整設計思路。其間由構思到實現,再到 優化,都對數據類結構進行比較大變動,目前比較穩定了,也項目中暫時夠用。在Vs2005進行了測試,生 成100萬個對象,仍然不至於造成的大量損耗,內存占用率與操作的時間都與普通對象相差不會太多。有 興趣的朋以也可以幫我做個測試,發現其中的一些問題,提出修改意見。在此先謝過了。
說到這邊,還沒有解決一個很大的問題,那就是編寫數據類的代碼太多了,特別上面的這樣設計,代 碼更是成倍的增長,也就是開發效率的問題。對此,我是使用CodeSmith,自定寫一個代碼生成模板,用 CodeSmith根據數據庫中的表的字段定義生成一個與之對應的數據類。利用它就可以做到不用寫一行代碼 就可以定義一個數據類,保證效率的同時,也保證了正確性。
關於數據類的定義,就介紹到這,下次將是介紹如何定義相應的數據集合。