在上篇Blog中介紹了如何定義一個與DataRow相結合的數據類,那麼本篇將介紹如何定義一個與 DataTable對應的數據集合。
在DotNet中提供了一個CollectionBase作為我們定義強類型的數據集合的抽象類,在DotNet1.1中要定 義一個強類型的數據集合就必須為每一種數據類定義一個對應的數據集合,在2.0中增加了泛型的功能後 ,這個問題得到了解決。又由於在目前的Ibatisnet版本中還不支持泛型的功能,所以數據集合可以選擇 從ArrayList或CollectionBase繼承下來。但是不管是ArrayList還是CollectionBase都不支持序列化,也 是他們都有沒有Serializable屬性,那麼實現的數據集合也將無法實現序列化,所以這邊選擇直接實現 IList接口,內部再實例化一個ArrayList做為數據容器。類的定義如下:
public class ObjectList : IList, ISerializable{}
還有一需要解決的問題是,數據類如何與DataTable對應起來呢?在數據類定義一個DataTable的屬性 和內部變量,做為存儲數據的容器。
1private DataTable m_dataTable = null;
2/**//// <summary>
3 /// Gets the data table form.
4 /// </summary>
5 /// <value>The data table form.</value>
6 public DataTable DataTableForm
7 {
8 get { return m_dataTable; }
9 }
10
因為前面有提到了,一個對象對應一個DataRow,當前一個對象單獨存在時,它的數據存放在數據類的 數據容器裡,而當中被加到一個集合裡時,數據行就應該存放在數據集合對應的DataTable裡了。那要怎 麼轉換呢?很簡單,只需要把數據行從原來(數據類的數據容器)的表裡面復制到ObjectList對應的 DataTable裡,然後將原來的行刪除就行了。這裡復制一行數據也是有學問的,因為一個DataRow一旦創建 了,它就只能被加入到創建它的那個DataTable裡,當它被加入到另外一個表裡就會拋出它同時只能屬於 一個表的異常,而如果是DataRow裡的數據一列一列地復制到另一個DataRow,就會對性能造成非常大的影 響。最理想的辦法就是數據不動,只改變DataRow記錄的一個狀態信息就行了。這裡的改變行狀態的辦法 就是NewRow,而數據不動的辦法就是將源Row的ItemArray屬性賦值給目的的ItemArray。經過測試,這樣 做可以減少1倍以上的時間成本(內存成本也一樣)。
具體的添加方法如下:
1private void AddObjectRowToListTable(IDataObject p_dataObject)
2 {
3 if (m_dataTable == null)
4 m_dataTable = p_dataObject.DataContainer.Clone();
5 DataRow m_newRow = m_dataTable.NewRow();
6 m_newRow.ItemArray = p_dataObject.ObjectRow.ItemArray;
7 /**//***********************************************
8 * 使用上面代 碼時間性能會比下面的高一倍
9 * for (int i = 0; i < p_dataObject.ObjectRow.ItemArray.Length; i++)
10 {
11 m_newRow[i] = p_dataObject.ObjectRow.ItemArray[i];
12 }
13 * ********************************************/
14
15 m_dataTable.Rows.Add(m_newRow);
16 p_dataObject.ObjectRow.Delete ();
17 p_dataObject.ObjectRow.Table.AcceptChanges();
18 p_dataObject.ObjectRow = m_newRow;
19 }
20
下一個就是序列化的問題了。序列化集合類好像比較麻煩,因為它本身並沒有為我提供支持序列化的 方法。而在這個數據集合裡,實現了ISerializable接口,由我們自己來定義要序列的方式。我們的關鍵 是序列化數據,所以就這裡的序列化就只需要序列ObjectList對應的DataTable就行了。實現 ISerializable接口如下:
1public void GetObjectData(SerializationInfo info, StreamingContext context)
2 {
3 if (this.Count >= 0)
4 {
5 info.AddValue("DataObjectType", m_list[0].GetType()); //當前數據類的類型
6 }
7
8 info.AddValue("DataTable", this.m_dataTable, typeof(DataTable));
9 }
10
同時增加一個反序列化的構造函數
1protected ObjectList(SerializationInfo info, StreamingContext context) : base()
2 {
3 m_dataTable = info.GetValue("DataTable", typeof(DataTable)) as DataTable;
4 Type m_dataObjectType = info.GetValue("DataObjectType", typeof(Type)) as Type;
5 if (m_dataObjectType != null)
6 {
7 foreach (DataRow m_dr in m_dataTable.Rows)
8 {
9 m_list.Add (Activator.CreateInstance(m_dataObjectType, m_dr));
10 }
11 }
12 }
13
這樣就可以支持序列化與反序列化了。
整個數據類的定義如下:
1[Serializable]
2 public class ObjectList1 : IList, ISerializable
3 {
4 private ArrayList m_list = null;
5 private DataTable m_dataTable = null;
6 /**//// <summary>
7 /// Initializes a new instance of the <see cref="T:ObjectList"/> class.
8 /// </summary>
9 public ObjectList1()
10 {
11 m_list = new ArrayList();
12 }
13 /**//// <summary>
14 /// Gets the data table form.
15 /// </summary>
16 /// <value>The data table form.</value>
17 public DataTable DataTableForm
18 {
19 get { return m_dataTable; }
20 }
21 private void AddObjectRowToListTable(IDataObject p_dataObject)
22 {
23 if (m_dataTable == null)
24 m_dataTable = p_dataObject.DataContainer.Clone();
25 DataRow m_newRow = m_dataTable.NewRow();
26
27
28 m_newRow.ItemArray = p_dataObject.ObjectRow.ItemArray;
29 /**//***********************************************
30 * 使用上面 代碼時間性能會比下面的高一倍
31 * for (int i = 0; i < p_dataObject.ObjectRow.ItemArray.Length; i++)
32 {
33 m_newRow[i] = p_dataObject.ObjectRow.ItemArray[i];
34 }
35 * ********************************************/
36
37 m_dataTable.Rows.Add(m_newRow);
38 p_dataObject.ObjectRow.Delete ();
39 p_dataObject.ObjectRow.Table.AcceptChanges();
40 p_dataObject.ObjectRow = m_newRow;
41 }
42 IList Members#region IList Members
43
44 /**//// <summary>
45 /// Adds an item to the <see cref="T:System.Collections.IList"></see>.
46 /// </summary>
47 /// <param name="value">The <see cref="T:System.Object"></see> to add to the <see cref="T:System.Collections.IList"></see>.</param>
48 /// <returns>
49 /// The position into which the new element was inserted.
50 /// </returns>
51 /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IList"></see> is read-only.-or- The <see cref="T:System.Collections.IList"></see> has a fixed size. </exception>
52 public int Add(object value)
53 {
54 AddObjectRowToListTable((IDataObject)value);
55 return this.m_list.Add(value);
56 }
57
58 /**//// <summary>
59 /// Removes all items from the <see cref="T:System.Collections.IList"></see>.
60 /// </summary>
61 /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IList"></see> is read-only. </exception>
62 public void Clear()
63 {
64 this.m_list.Clear();
65 this.DataTableForm.Clear();
66 }
67
68 /**//// <summary>
69 /// Determines whether the <see cref="T:System.Collections.IList"></see> contains a specific value.
70 /// </summary>
71 /// <param name="value">The <see cref="T:System.Object"></see> to locate in the <see cref="T:System.Collections.IList"></see>.</param>
72 /// <returns>
73 /// true if the <see cref="T:System.Object"></see> is found in the <see cref="T:System.Collections.IList"></see>; otherwise, false.
74 /// </returns>
75 public bool Contains(object value)
76 {
77 return m_list.Contains(value);
78 }
79
80 /**//// <summary>
81 /// Determines the index of a specific item in the <see cref="T:System.Collections.IList"></see>.
82 /// </summary>
83 /// <param name="value">The <see cref="T:System.Object"></see> to locate in the <see cref="T:System.Collections.IList"></see>.</param>
84 /// <returns>
85 /// The index of value if found in the list; otherwise, -1.
86 /// </returns>
87 public int IndexOf(object value)
88 {
89 return m_list.IndexOf(value);
90 }
91
92 /**//// <summary>
93 /// Inserts an item to the <see cref="T:System.Collections.IList"></see> at the specified index.
94 /// </summary>
95 /// <param name="index">The zero-based index at which value should be inserted.</param>
96 /// <param name="value">The <see cref="T:System.Object"></see> to insert into the <see cref="T:System.Collections.IList"></see>.</param>
97 /// <exception cref="T:System.ArgumentOutOfRangeException">index is not a valid index in the <see cref="T:System.Collections.IList"></see>. </exception>
98 /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IList"></see> is read-only.-or- The <see cref="T:System.Collections.IList"></see> has a fixed size. </exception>
99 /// <exception cref="T:System.NullReferenceException">value is null reference in the <see cref="T:System.Collections.IList"></see>.</exception>
100 public void Insert(int index, object value)
101 {
102 this.m_list.Insert(index, value);
103 AddObjectRowToListTable ((IDataObject)value);
104 }
105
106 /**//// <summary>
107 /// Gets a value indicating whether the <see cref="T:System.Collections.IList"></see> has a fixed size.
108 /// </summary>
109 /// <value></value>
110 /// <returns>true if the <see cref="T:System.Collections.IList"></see> has a fixed size; otherwise, false.</returns>
111 public bool IsFixedSize
112 {
113 get { return false; }
114 }
115
116 /**//// <summary>
117 /// Gets a value indicating whether the <see cref="T:System.Collections.IList"></see> is read-only.
118 /// </summary>
119 /// <value></value>
120 /// <returns>true if the <see cref="T:System.Collections.IList"></see> is read-only; otherwise, false.</returns>
121 public bool IsReadOnly
122 {
123 get { return false; }
124 }
125
126 /**//// <summary>
127 /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.IList"></see>.
128 /// </summary>
129 /// <param name="value">The <see cref="T:System.Object"></see> to remove from the <see cref="T:System.Collections.IList"></see>.</param>
130 /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IList"></see> is read-only.-or- The <see cref="T:System.Collections.IList"></see> has a fixed size. </exception>
131 public void Remove(object value)
132 {
133 ((IDataObject)value).ObjectRow.Delete();
134 this.m_list.Remove(value);
135 }
136
137 /**//// <summary>
138 /// Removes the <see cref="T:System.Collections.IList"></see> item at the specified index.
139 /// </summary>
140 /// <param name="index">The zero- based index of the item to remove.</param>
141 /// <exception cref="T:System.ArgumentOutOfRangeException">index is not a valid index in the <see cref="T:System.Collections.IList"></see>. </exception>
142 /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.IList"></see> is read-only.-or- The <see cref="T:System.Collections.IList"></see> has a fixed size. </exception>
143 public void RemoveAt(int index)
144 {
145 ((IDataObject)this.m_list[index]).ObjectRow.Delete();
146 this.m_list.RemoveAt(index);
147 }
148
149 /**//// <summary>
150 /// Gets or sets the <see cref="T:Object"/> at the specified index.
151 /// </summary>
152 /// <value></value>
153 public object this[int index]
154 {
155 get { return this.m_list[index]; }
156 set
157 {
158 throw new Exception("The method or operation is not implemented.");
159 }
160 }
161
162 #endregion
163
164 ICollection Members#region ICollection Members
165
166 /**//// <summary>
167 /// Copies the elements of the <see cref="T:System.Collections.ICollection"></see> to an <see cref="T:System.Array"></see>, starting at a particular <see cref="T:System.Array"></see> index.
168 /// </summary>
169 /// <param name="array">The one-dimensional <see cref="T:System.Array"></see> that is the destination of the elements copied from <see cref="T:System.Collections.ICollection"></see>. The <see cref="T:System.Array"></see> must have zero-based indexing.</param>
170 /// <param name="index">The zero-based index in array at which copying begins.</param>
171 /// <exception cref="T:System.ArgumentNullException">array is null. </exception>
172 /// <exception cref="T:System.ArgumentOutOfRangeException">index is less than zero. </exception>
173 /// <exception cref="T:System.ArgumentException">array is multidimensional.-or- index is equal to or greater than the length of array.-or- The number of elements in the source <see cref="T:System.Collections.ICollection"></see> is greater than the available space from index to the end of the destination array. </exception>
174 /// <exception cref="T:System.InvalidCastException">The type of the source <see cref="T:System.Collections.ICollection"></see> cannot be cast automatically to the type of the destination array. </exception>
175 public void CopyTo (Array array, int index)
176 {
177 this.m_list.CopyTo(array, index);
178 }
179
180 /**//// <summary>
181 /// Gets the number of elements contained in the <see cref="T:System.Collections.ICollection"></see>.
182 /// </summary>
183 /// <value></value>
184 /// <returns>The number of elements contained in the <see cref="T:System.Collections.ICollection"></see>.</returns>
185 public int Count
186 {
187 get { return this.m_list.Count; }
188 }
189
190 /**//// <summary>
191 /// Gets a value indicating whether access to the <see cref="T:System.Collections.ICollection"></see> is synchronized (thread safe).
192 /// </summary>
193 /// <value></value>
194 /// <returns>true if access to the <see cref="T:System.Collections.ICollection"></see> is synchronized (thread safe); otherwise, false.</returns>
195 public bool IsSynchronized
196 {
197 get { return false; }
198 }
199
200 /**//// <summary>
201 /// Gets an object that can be used to synchronize access to the <see cref="T:System.Collections.ICollection"></see>.
202 /// </summary>
203 /// <value></value>
204 /// <returns>An object that can be used to synchronize access to the <see cref="T:System.Collections.ICollection"></see>.</returns>
205 public object SyncRoot
206 {
207 get { return this.m_list.SyncRoot; }
208 }
209
210 #endregion
211
212 IEnumerable Members#region IEnumerable Members
213
214 /**//// <summary>
215 /// Returns an enumerator that iterates through a collection.
216 /// </summary>
217 /// <returns>
218 /// An <see cref="T:System.Collections.IEnumerator"></see> object that can be used to iterate through the collection.
219 /// </returns>
220 public IEnumerator GetEnumerator()
221 {
222 return m_list.GetEnumerator();
223 }
224
225 #endregion
226
227 protected ObjectList1(SerializationInfo info, StreamingContext context)
228 {
229 m_dataTable = info.GetValue("DataTable", typeof(DataTable)) as DataTable;
230 Type m_dataObjectType = info.GetValue("DataObjectType", typeof(Type)) as Type;
231 if (m_dataObjectType != null)
232 {
233 m_list = new ArrayList();
234 foreach (DataRow m_dr in m_dataTable.Rows)
235 {
236 m_list.Add(Activator.CreateInstance (m_dataObjectType,m_dr));
237 }
238 }
239 }
240 ISerializable Members#region ISerializable Members
241
242 public void GetObjectData(SerializationInfo info, StreamingContext context)
243 {
244 if (m_list.Count >= 0)
245 {
246 info.AddValue("DataObjectType", this.m_list[0].GetType());
247 }
248
249 info.AddValue("DataTable", this.m_dataTable, typeof (DataTable));
250 }
251
252 #endregion
253 }
上面的數據類肯定還存在一些問題,目前沒有更深入地去研究,有些地方可能還不是很合理,隨著應 用的深入,仍需要做一些改進。最後,如果認為這樣使用很不爽,不能做到強類型的引用。所有的對象都 只能認識到DataObjectBase這一級類型(因為所有的數據類都是這個抽象裡繼承下來)。我們可以定義一個 泛型的集合對它進行包裝,這裡就不多做介紹了。