在上篇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这一级类型(因为所有的数据类都是这个抽象里继承下来)。我们可以定义一个泛型的集合对它进行包装,这里就不多做介绍了。
快速索引
与DotNet数据对象结合的自定义数据对象设计 (一) 数据对象与DataRow