众所周知,DataSet里面存放的实际上就是一堆的xml,当然,光说xml的文件宽泛了,实际上是用Xml描述的DataTable, DataColumn,, DataSet。在没有动作以前,DataSet里面的东西都是XmlSchema,当我们在WebService里面调用的时候,那么一堆的XmlSchema带着数据,就一起在网络上传输了,这样拖家带口的,肯定就慢,而且还有不少的冗余信息,所以在老外经过对DataSet里面进行解剖以后,就发现了一个比较有效的方法,分离。
至于分离,主要分离的是Schema和data,这样拆开来做,不仅减少了数据的冗余,而且提高了传输的效率,打成流的东西,想不快都难。谁叫网络上传输的都是0和1。当然,里面也要注意还原性,有些网站上给例子,竟然把序列化以后的流给toArray()转化,试想,序列化以后的东西如果进行了转化,那么数据进行了填充,本来用八位表示一个一的,转化后用十六位表示了,那传输过去,反序列化肯定失败,因为本质变了,数据被拆分填充了,再怎么变肯定也变不回来了,当然,你bt一点还是可以变回来,但是你要付出一个怎样的代价,也就不好估计了。所以说,对于序列化后的在流里面的东西,你最好别动。
还有一个要注意的地方是在分离和重组的时候,Table的强制性约束和只读属性一定要撤了,读完或者写完在加上,要不然你就只有抱着异常去哭了。
好了,这样就把该搞定的搞定了,最后加个二进制序列化器,打成流就可以了,然后就开始传输精神和肉体分离的数据了,到接收端再反序列化,然后进行组装,这样的话就很ok了,微软的demo里面有个程序可以马上进行测试,速度的确快了不少。我现在也贴点源码,感觉对DataSet,DataTable的结构理解还是很有帮助的。至于序列化的,
下面是两个方法的调用,一个是序列化发,一个是反序列化收的。
public byte[] GetBinaryFormatData(DataSet m_ds)
{
byte[] binaryDataResult = null;
MemoryStream memStream = new MemoryStream();
IFormatter m_formatter = new BinaryFormatter();
DataSetSurrogate dss = new DataSetSurrogate(m_ds);
m_formatter.Serialize(memStream, dss);
memStream.Write(binaryDataResult, 0, memStream.Length);
memStream.Close();
memStream.Dispose();
return binaryDataResult;
}
public DataSet RetrieveDataSet(byte[] binaryData)
{
DataSet dataSetResult=null;
DataSetSurrogate dataSurrogateSet = null;
MemoryStream memStream = new MemoryStream(binaryData);
IFormatter brFormatter = new BinaryFormatter();
object obj = brFormatter.Deserialize(memStream);
dataSurrogateSet = obj as DataSetSurrogate;
dataSurrogateSet.ReadDataIntoDataSet(dataSetResult);
return dataSetResult;
}
最后附上类的实现代码,注:是个老外03年写的,牛人啊。
――――――――
using System;
using System.IO;
using System.ComponentModel;
using System.Collections;
using System.Globalization;
using System.Xml;
using System.Data;
using System.Runtime.Serialization;
using System.Diagnostics;
/*
Author : Ravinder Vuppula.
Purpose : To implement binary serialization of the DataSet through a Surrogate object.
Notes:
1. All the surrogate objects DataSetSurrogate, DataTableSurrogate, DataColumnSurrogate are marked [Serializable] and hence will get automatically serialized by the remoting framework.
2. The data is serialized in binary "column" wise.
3. This class can be used as a wrapper around DataSet. A DataSetSurrogate object can be constructed from DataSet and vice-versa. This helps if the user wants to wrap the DataSet in DataSetSurrogate and serialize and deserialize DataSetSurrogate instead.
History:
05/10/04 - Fix for the issue of serializing default values.
*/
[Serializable]
public class DataSetSurrogate {
//DataSet properties
private string _datasetName;
private string _namespace;
private string _prefix;
private bool _caseSensitive;
private CultureInfo _locale;
private bool _enforceConstraints;
//ForeignKeyConstraints
private ArrayList _fkConstraints;//An ArrayList of foreign key constraints : [constraintName]->[parentTableIndex, parentcolumnIndexes]->[childTableIndex, childColumnIndexes]->[AcceptRejectRule, UpdateRule, Delete]->[extendedProperties]
//Relations
private ArrayList _relations;//An ArrayList of foreign key constraints : [relationName]->[parentTableIndex, parentcolumnIndexes]->[childTableIndex, childColumnIndexes]->[Nested]->[extendedProperties]
//ExtendedProperties
private Hashtable _extendedProperties;
//Columns and Rows
private DataTableSurrogate[] _dataTableSurrogates;
/*
Constructs a DataSetSurrogate object from a DataSet.
*/
public DataSetSurrogate(DataSet ds) {
if (ds == null) {
throw new ArgumentNullException("The parameter dataset is null");
}
//DataSet properties
_datasetName = ds.DataSetName;
_namespace = ds.Namespace;
_prefix = ds.Prefix;
_caseSensitive = ds.CaseSensitive;
_locale = ds.Locale;
_enforceConstraints = ds.EnforceConstraints;
//Tables, Columns, Rows
_dataTableSurrogates = new DataTableSurrogate[ds.Tables.Count];
for (int i = 0; i < ds.Tables.Count; i++) {
_dataTableSurrogates[i] = new DataTableSurrogate(ds.Tables[i]);
}
//ForeignKeyConstraints
_fkConstraints = GetForeignKeyConstraints(ds);
//Relations
_relations = GetRelations(ds);
//ExtendedProperties
_extendedProperties = new Hashtable();
if (ds.ExtendedProperties.Keys.Count > 0) {
foreach (object propertyKey in ds.ExtendedProperties.Keys) {
_extendedProperties.Add(propertyKey, ds.ExtendedProperties[propertyKey]);
}
}
}
/*
Constructs a DataSet from the DataSetSurrogate object. This can be used after the user recieves a Surrogate object over the wire and wished to construct a DataSet from it.
*/
public DataSet ConvertToDataSet() {
DataSet ds = new DataSet();
ReadSchemaIntoDataSet(ds);
ReadDataIntoDataSet(ds);
return ds;
}
/*
Reads the schema into the dataset from the DataSetSurrogate object.
*/
public void ReadSchemaIntoDataSet(DataSet ds) {
if (ds == null) {
throw new ArgumentNullException("The dataset parameter cannot be null");
}
//DataSet properties
ds.DataSetName = _datasetName;
ds.Namespace = _namespace;
ds.Prefix = _prefix;
ds.CaseSensitive = _caseSensitive;
ds.Locale = _locale;
ds.EnforceConstraints = _enforceConstraints;
//Tables, Columns
Debug.Assert(_dataTableSurrogates != null);
foreach (DataTableSurrogate dataTableSurrogate in _dataTableSurrogates) {
DataTable dt = new DataTable();
dataTableSurrogate.ReadSchemaIntoDataTable(dt);
ds.Tables.Add(dt);
}
//ForeignKeyConstraints
SetForeignKeyConstraints(ds, _fkConstraints);
//Relations
SetRelations(ds, _relations);
//Set ExpressionColumns
Debug.Assert(_dataTableSurrogates != null);
Debug.Assert(ds.Tables.Count == _dataTableSurrogates.Length);
for (int i = 0; i < ds.Tables.Count; i++) {
DataTable dt = ds.Tables[i];
DataTableSurrogate dataTableSurrogate = _dataTableSurrogates[i];
dataTableSurrogate.SetColumnExpressions(dt);
}
//ExtendedProperties
Debug.Assert(_extendedProperties != null);
if (_extendedProperties.Keys.Count > 0) {
foreach (object propertyKey in _extendedProperties.Keys) {
ds.ExtendedProperties.Add(propertyKey, _extendedProperties[propertyKey]);
}
}
}
/*
Reads the data into the dataset from the DataSetSurrogate object.
*/
public void ReadDataIntoDataSet(DataSet ds) {
if (ds == null) {
throw new ArgumentNullException("The dataset parameter cannot be null");
}
//Suppress read-only columns and constraint rules when loading the data
ArrayList readOnlyList = SuppressReadOnly(ds);
ArrayList constraintRulesList = SuppressConstraintRules(ds);
//Rows
Debug.Assert(IsSchemaIdentical(ds));
Debug.Assert(_dataTableSurrogates != null);
Debug.Assert(ds.Tables.Count == _dataTableSurrogates.Length);
bool enforceConstraints = ds.EnforceConstraints;
ds.EnforceConstraints = false;
for (int i = 0; i < ds.Tables.Count; i++) {
DataTable dt = ds.Tables[i];
DataTableSurrogate dataTableSurrogate = _dataTableSurrogates[i];
dataTableSurrogate.ReadDataIntoDataTable(ds.Tables[i], false);
}
ds.EnforceConstraints = enforceConstraints;
//Reset read-only columns and constraint rules back after loading the data
ResetReadOnly(ds, readOnlyList);
ResetConstraintRules(ds, constraintRulesList);
}
/*
Gets foreignkey constraints availabe on the tables in the dataset.
***Serialized foreign key constraints format : [constraintName]->[parentTableIndex, parentcolumnIndexes]->[childTableIndex, childColumnIndexes]->[AcceptRejectRule, UpdateRule, Delete]->[extendedProperties]***
*/
private ArrayList GetForeignKeyConstraints(DataSet ds) {
Debug.Assert(ds != null);
ArrayList constraintList = new ArrayList();
for (int i = 0; i < ds.Tables.Count; i++) {
DataTable dt = ds.Tables[i];
for (int j = 0; j < dt.Constraints.Count; j++) {
Constraint c = dt.Constraints[j];
ForeignKeyConstraint fk = c as ForeignKeyConstraint;
if (fk != null) {
string constraintName = c.ConstraintName;
int[] parentInfo = new int[fk.RelatedColumns.Length + 1];
parentInfo[0] = ds.Tables.IndexOf(fk.RelatedTable);
for (int k = 1; k < parentInfo.Length; k++) {
parentInfo[k] = fk.RelatedColumns[k - 1].Ordinal;
}
int[] childInfo = new int[fk.Columns.Length + 1];
childInfo[0] = i;//Since the constraint is on the current table, this is the child table.
for (int k = 1; k < childInfo.Length; k++) {
childInfo[k] = fk.Columns[k - 1].Ordinal;
}
ArrayList list = new ArrayList();
list.Add(constraintName);
list.Add(parentInfo);
list.Add(childInfo);
list.Add(new int[] { (int) fk.AcceptRejectRule, (int) fk.UpdateRule, (int) fk.DeleteRule });
Hashtable extendedProperties = new Hashtable();
if (fk.ExtendedProperties.Keys.Count > 0) {
foreach (object propertyKey in fk.ExtendedProperties.Keys) {
extendedProperties.Add(propertyKey, fk.ExtendedProperties[propertyKey]);
}
}
list.Add(extendedProperties);
constraintList.Add(list);
}
}
}
return constraintList;
}
/*
Adds foreignkey constraints to the tables in the dataset. The arraylist contains the serialized format of the foreignkey constraints.
***Deserialize the foreign key constraints format : [constraintName]->[parentTableIndex, parentcolumnIndexes]->[childTableIndex, childColumnIndexes]->[AcceptRejectRule, UpdateRule, Delete]->[extendedProperties]***
*/
private void SetForeignKeyConstraints(DataSet ds, ArrayList constraintList) {
Debug.Assert(ds != null);
Debug.Assert(constraintList != null);
foreach (ArrayList list in constraintList) {
Debug.Assert(list.Count == 5);
string constraintName = (string) list[0];
int[] parentInfo = (int[]) list[1];
int[] childInfo = (int[]) list[2];
int[] rules = (int[]) list[3];
Hashtable extendedProperties = (Hashtable) list[4];
//ParentKey Columns.
Debug.Assert(parentInfo.Length >= 1);
DataColumn[] parentkeyColumns = new DataColumn[parentInfo.Length - 1];
for (int i = 0; i < parentkeyColumns.Length; i++) {
Debug.Assert(ds.Tables.Count > parentInfo[0]);
Debug.Assert(ds.Tables[parentInfo[0]].Columns.Count > parentInfo[i + 1]);
parentkeyColumns[i] = ds.Tables[parentInfo[0]].Columns[parentInfo[i + 1]];
}
//ChildKey Columns.
Debug.Assert(childInfo.Length >= 1);
DataColumn[] childkeyColumns = new DataColumn[childInfo.Length - 1];
for (int i = 0; i < childkeyColumns.Length; i++) {
Debug.Assert(ds.Tables.Count > childInfo[0]);
Debug.Assert(ds.Tables[childInfo[0]].Columns.Count > childInfo[i + 1]);
childkeyColumns[i] = ds.Tables[childInfo[0]].Columns[childInfo[i + 1]];
}
//Create the Constraint.
ForeignKeyConstraint fk = new ForeignKeyConstraint(constraintName, parentkeyColumns, childkeyColumns);
Debug.Assert(rules.Length == 3);
fk.AcceptRejectRule = (AcceptRejectRule) rules[0];
fk.UpdateRule = (Rule) rules[1];
fk.DeleteRule = (Rule) rules[2];
//Extended Properties.
Debug.Assert(extendedProperties != null);
if (extendedProperties.Keys.Count > 0) {
foreach (object propertyKey in extendedProperties.Keys) {
fk.ExtendedProperties.Add(propertyKey, extendedProperties[propertyKey]);
}
}
//Add the constraint to the child datatable.
Debug.Assert(ds.Tables.Count > childInfo[0]);
ds.Tables[childInfo[0]].Constraints.Add(fk);
}
}
/*
Gets relations from the dataset.
***Serialized relations format : [relationName]->[parentTableIndex, parentcolumnIndexes]->[childTableIndex, childColumnIndexes]->[Nested]->[extendedProperties]***
*/
private ArrayList GetRelations(DataSet ds) {
Debug.Assert(ds != null);
ArrayList relationList = new ArrayList();
foreach (DataRelation rel in ds.Relations) {
string relationName = rel.RelationName;
int[] parentInfo = new int[rel.ParentColumns.Length + 1];
parentInfo[0] = ds.Tables.IndexOf(rel.ParentTable);
for (int j = 1; j < parentInfo.Length; j++) {
parentInfo[j] = rel.ParentColumns[j - 1].Ordinal;
}
int[] childInfo = new int[rel.ChildColumns.Length + 1];
childInfo[0] = ds.Tables.IndexOf(rel.ChildTable);
for (int j = 1; j < childInfo.Length; j++) {
childInfo[j] = rel.ChildColumns[j - 1].Ordinal;
}
ArrayList list = new ArrayList();
list.Add(relationName);
list.Add(parentInfo);
list.Add(childInfo);
list.Add(rel.Nested);
Hashtable extendedProperties = new Hashtable();
if (rel.ExtendedProperties.Keys.Count > 0) {
foreach (object propertyKey in rel.ExtendedProperties.Keys) {
extendedProperties.Add(propertyKey, rel.ExtendedProperties[propertyKey]);
}
}
list.Add(extendedProperties);
relationList.Add(list);
}
return relationList;
}
/*
Adds relations to the dataset. The arraylist contains the serialized format of the relations.
***Deserialize the relations format : [relationName]->[parentTableIndex, parentcolumnIndexes]->[childTableIndex, childColumnIndexes]->[Nested]->[extendedProperties]***
*/
private void SetRelations(DataSet ds, ArrayList relationList) {
Debug.Assert(ds != null);
Debug.Assert(relationList != null);
foreach (ArrayList list in relationList) {
Debug.Assert(list.Count == 5);
string relationName = (string) list[0];
int[] parentInfo = (int[]) list[1];
int[] childInfo = (int[]) list[2];
bool isNested = (bool) list[3];
Hashtable extendedProperties = (Hashtable) list[4];
//ParentKey Columns.
Debug.Assert(parentInfo.Length >= 1);
DataColumn[] parentkeyColumns = new DataColumn[parentInfo.Length - 1];
for (int i = 0; i < parentkeyColumns.Length; i++) {
Debug.Assert(ds.Tables.Count > parentInfo[0]);
Debug.Assert(ds.Tables[parentInfo[0]].Columns.Count > parentInfo[i + 1]);
parentkeyColumns[i] = ds.Tables[parentInfo[0]].Columns[parentInfo[i + 1]];
}
//ChildKey Columns.
Debug.Assert(childInfo.Length >= 1);
DataColumn[] childkeyColumns = new DataColumn[childInfo.Length - 1];
for (int i = 0; i < childkeyColumns.Length; i++) {
Debug.Assert(ds.Tables.Count > childInfo[0]);
Debug.Assert(ds.Tables[childInfo[0]].Columns.Count > childInfo[i + 1]);
childkeyColumns[i] = ds.Tables[childInfo[0]].Columns[childInfo[i + 1]];
}
//Create the Relation, without any constraints[Assumption: The constraints are added earlier than the relations]
DataRelation rel = new DataRelation(relationName, parentkeyColumns, childkeyColumns, false);
rel.Nested = isNested;
//Extended Properties.
Debug.Assert(extendedProperties != null);
if (extendedProperties.Keys.Count > 0) {
foreach (object propertyKey in extendedProperties.Keys) {
rel.ExtendedProperties.Add(propertyKey, extendedProperties[propertyKey]);
}
}
//Add the relations to the dataset.
ds.Relations.Add(rel);
}
}
/*
Suppress the read-only property and returns an arraylist of read-only columns.
*/
private ArrayList SuppressReadOnly(DataSet ds) {
Debug.Assert(ds != null);
ArrayList readOnlyList = new ArrayList();
for (int i = 0; i < ds.Tables.Count; i++) {
DataTable dt = ds.Tables[i];
for (int j = 0; j < dt.Columns.Count; j++) {
if (dt.Columns[j].Expression == String.Empty && dt.Columns[j].ReadOnly == true) {
dt.Columns[j].ReadOnly = false;
readOnlyList.Add(new int[] { i, j });
}
}
}
return readOnlyList;
}
/*
Suppress the foreign key constraint rules and returns an arraylist of the existing foreignkey constraint rules.
*/
private ArrayList SuppressConstraintRules(DataSet ds) {
Debug.Assert(ds != null);
ArrayList constraintRulesList = new ArrayList();
for (int i = 0; i < ds.Tables.Count; i++) {
DataTable dtChild = ds.Tables[i];
for (int j = 0; j < dtChild.Constraints.Count; j++) {
Constraint c = dtChild.Constraints[j];
if (c is ForeignKeyConstraint) {
ForeignKeyConstraint fk = (ForeignKeyConstraint) c;
ArrayList list = new ArrayList();
list.Add(new int[] { i, j });
list.Add(new int[] { (int) fk.AcceptRejectRule, (int) fk.UpdateRule, (int) fk.DeleteRule });
constraintRulesList.Add(list);
fk.AcceptRejectRule = AcceptRejectRule.None;
fk.UpdateRule = Rule.None;
fk.DeleteRule = Rule.None;
}
}
}
return constraintRulesList;
}
/*
Resets the read-only columns on the datatable based on the input readOnly list.
*/
private void ResetReadOnly(DataSet ds, ArrayList readOnlyList) {
Debug.Assert(ds != null);
Debug.Assert(readOnlyList != null);
foreach (object o in readOnlyList) {
int[] indicesArr = (int[]) o;
Debug.Assert(indicesArr.Length == 2);
int tableIndex = indicesArr[0];
int columnIndex = indicesArr[1];
Debug.Assert(ds.Tables.Count > tableIndex);
Debug.Assert(ds.Tables[tableIndex].Columns.Count > columnIndex);
DataColumn dc = ds.Tables[tableIndex].Columns[columnIndex];
Debug.Assert(dc != null);
dc.ReadOnly = true;
}
}
/*
Resets the foreignkey constraint rules on the dataset based on the input constraint rules list.
*/
private void ResetConstraintRules(DataSet ds, ArrayList constraintRulesList) {
Debug.Assert(ds != null);
Debug.Assert(constraintRulesList != null);
foreach (ArrayList list in constraintRulesList) {
Debug.Assert(list.Count == 2);
int[] indicesArr = (int[]) list[0];
int[] rules = (int[]) list[1];
Debug.Assert(indicesArr.Length == 2);
int tableIndex = indicesArr[0];
int constraintIndex = indicesArr[1];
Debug.Assert(ds.Tables.Count > tableIndex);
DataTable dtChild = ds.Tables[tableIndex];
Debug.Assert(dtChild.Constraints.Count > constraintIndex);
ForeignKeyConstraint fk = (ForeignKeyConstraint) dtChild.Constraints[constraintIndex];
Debug.Assert(rules.Length == 3);
fk.AcceptRejectRule = (AcceptRejectRule) rules[0];
fk.UpdateRule = (Rule) rules[1];
fk.DeleteRule = (Rule) rules[2];
}
}
/*
Checks whether the dataset name and namespaces are as expected and the tables count is right.
*/
private bool IsSchemaIdentical(DataSet ds) {
Debug.Assert(ds != null);
if (ds.DataSetName != _datasetName || ds.Namespace != _namespace) {
return false;
}
Debug.Assert(_dataTableSurrogates != null);
if (ds.Tables.Count != _dataTableSurrogates.Length) {
return false;
}
return true;
}
}
[Serializable]
class DataTableSurrogate {
//DataTable properties
private string _tableName;
private string _namespace;
private string _prefix;
private bool _caseSensitive;
private CultureInfo _locale;
private string _displayExpression;
private int _minimumCapacity;
//Columns
private DataColumnSurrogate[] _dataColumnSurrogates;
//Constraints
private ArrayList _uniqueConstraints; //An ArrayList of unique constraints : [constraintName]->[columnIndexes]->[IsPrimaryKey]->[extendedProperties]
//ExtendedProperties
private Hashtable _extendedProperties;
//Rows
private BitArray _rowStates; //The 4 rowstates[Unchanged, Added, Modified, Deleted] are represented with 2 bits. The length of the BitArray will be twice the size of the number of rows.
private object[][] _records; //As many object[] as there are number of columns. Always send 2 records for 1 row. TradeOff between memory vs. performance. Time intensive to find which records are modified.
private Hashtable _rowErrors = new Hashtable(); //Keep a map between the row index and the row error
private Hashtable _colErrors = new Hashtable(); //Keep a map between the row index and the Arraylist of columns that are in error and the error strings.
/*
Constructs a DataTableSurrogate from a DataTable.
*/
public DataTableSurrogate(DataTable dt) {
if (dt == null) {
throw new ArgumentNullException("The parameter dt is null");
}
_tableName = dt.TableName;
_namespace = dt.Namespace;
_prefix = dt.Prefix;
_caseSensitive = dt.CaseSensitive;
_locale = dt.Locale;
_displayExpression = dt.DisplayExpression;
_minimumCapacity = dt.MinimumCapacity;
//Columns
_dataColumnSurrogates = new DataColumnSurrogate[dt.Columns.Count];
for (int i = 0; i < dt.Columns.Count; i++) {
_dataColumnSurrogates[i] = new DataColumnSurrogate(dt.Columns[i]);
}
//Constraints
_uniqueConstraints = GetUniqueConstraints(dt);
//ExtendedProperties
_extendedProperties = new Hashtable();
if (dt.ExtendedProperties.Keys.Count > 0) {
foreach (object propertyKey in dt.ExtendedProperties.Keys) {
_extendedProperties.Add(propertyKey, dt.ExtendedProperties[propertyKey]);
}
}
//Rows
if (dt.Rows.Count > 0) {
_rowStates = new BitArray(dt.Rows.Count << 1);
_records = new object[dt.Columns.Count] [];
for (int i = 0; i < dt.Columns.Count; i++) {
_records[i] = new object[dt.Rows.Count << 1];
}
for (int i = 0; i < dt.Rows.Count; i++) {
GetRecords(dt.Rows[i], i << 1);
}
}
}
/*
Constructs a DataTable from DataTableSurrogate.
*/
public DataTable ConvertToDataTable() {
DataTable dt = new DataTable();
ReadSchemaIntoDataTable(dt);
ReadDataIntoDataTable(dt);
return dt;
}
/*
Reads the schema into the datatable from DataTableSurrogate.
*/
public void ReadSchemaIntoDataTable(DataTable dt) {
if (dt == null) {
throw new ArgumentNullException("The datatable parameter cannot be null");
}
dt.TableName = _tableName;
dt.Namespace = _namespace;
dt.Prefix = _prefix;
dt.CaseSensitive = _caseSensitive;
dt.Locale = _locale;
dt.DisplayExpression = _displayExpression;
dt.MinimumCapacity = _minimumCapacity;
Debug.Assert(_dataColumnSurrogates != null);
for (int i = 0; i < _dataColumnSurrogates.Length; i++) {
DataColumnSurrogate dataColumnSurrogate = _dataColumnSurrogates[i];
DataColumn dc = dataColumnSurrogate.ConvertToDataColumn();
dt.Columns.Add(dc);
}
//UniqueConstraints
SetUniqueConstraints(dt, _uniqueConstraints);
//Extended properties
Debug.Assert(_extendedProperties != null);
if (_extendedProperties.Keys.Count > 0) {
foreach (object propertyKey in _extendedProperties.Keys) {
dt.ExtendedProperties.Add(propertyKey, _extendedProperties[propertyKey]);
}
}
}
/*
Reads the data into a DataTable from DataTableSurrogate.
*/
public void ReadDataIntoDataTable(DataTable dt) {
ReadDataIntoDataTable(dt, true);
}
/*
Copies the rows into a DataTable from DataTableSurrogate.
*/
internal void ReadDataIntoDataTable(DataTable dt, bool suppressSchema) {
if (dt == null) {
throw new ArgumentNullException("The datatable parameter cannot be null");
}
Debug.Assert(IsSchemaIdentical(dt));
//Suppress read-only and constraint rules while loading the data.
ArrayList readOnlyList = null;
ArrayList constraintRulesList = null;
if (suppressSchema) {
readOnlyList = SuppressReadOnly(dt);
constraintRulesList = SuppressConstraintRules(dt);
}
//Read the rows
if (_records != null && dt.Columns.Count > 0) {
Debug.Assert(_records.Length > 0);
int rowCount = _records[0].Length >> 1;
for (int i = 0; i < rowCount; i++) {
ConvertToDataRow(dt, i << 1);
}
}
//Reset read-only column and constraint rules back after loading the data.
if (suppressSchema) {
ResetReadOnly(dt, readOnlyList);
ResetConstraintRules(dt, constraintRulesList);
}
}
/*
Gets unique constraints availabe on the datatable.
***Serialized unique constraints format : [constraintName]->[columnIndexes]->[IsPrimaryKey]->[extendedProperties]***
*/
private ArrayList GetUniqueConstraints(DataTable dt) {
Debug.Assert(dt != null);
ArrayList constraintList = new ArrayList();
for (int i = 0; i < dt.Constraints.Count; i++) {
Constraint c = dt.Constraints[i];
UniqueConstraint uc = c as UniqueConstraint;
if (uc != null) {
string constraintName = c.ConstraintName;
int[] colInfo = new int[uc.Columns.Length];
for (int j = 0; j < colInfo.Length; j++) {
colInfo[j] = uc.Columns[j].Ordinal;
}
ArrayList list = new ArrayList();
list.Add(constraintName);
list.Add(colInfo);
list.Add(uc.IsPrimaryKey);
Hashtable extendedProperties = new Hashtable();
if (uc.ExtendedProperties.Keys.Count > 0) {
foreach (object propertyKey in uc.ExtendedProperties.Keys) {
extendedProperties.Add(propertyKey, uc.ExtendedProperties[propertyKey]);
}
}
list.Add(extendedProperties);
constraintList.Add(list);
}
}
return constraintList;
}
/*
Adds unique constraints to the table. The arraylist contains the serialized format of the unique constraints.
***Deserialize the unique constraints format : [constraintName]->[columnIndexes]->[IsPrimaryKey]->[extendedProperties]***
*/
private void SetUniqueConstraints(DataTable dt, ArrayList constraintList) {
Debug.Assert(dt != null);
Debug.Assert(constraintList != null);
foreach (ArrayList list in constraintList) {
Debug.Assert(list.Count == 4);
string constraintName = (string) list[0];
int[] keyColumnIndexes = (int[]) list[1];
bool isPrimaryKey = (bool) list[2];
Hashtable extendedProperties = (Hashtable) list[3];
DataColumn[] keyColumns = new DataColumn[keyColumnIndexes.Length];
for (int i = 0; i < keyColumnIndexes.Length; i++) {
Debug.Assert(dt.Columns.Count > keyColumnIndexes[i]);
keyColumns[i] = dt.Columns[keyColumnIndexes[i]];
}
//Create the constraint.
UniqueConstraint uc = new UniqueConstraint(constraintName, keyColumns, isPrimaryKey);
//Extended Properties.
Debug.Assert(extendedProperties != null);
if (extendedProperties.Keys.Count > 0) {
foreach (object propertyKey in extendedProperties.Keys) {
uc.ExtendedProperties.Add(propertyKey, extendedProperties[propertyKey]);
}
}
dt.Constraints.Add(uc);
}
}
/*
Sets expression on the columns.
*/
internal void SetColumnExpressions(DataTable dt) {
Debug.Assert(dt != null);
Debug.Assert(_dataColumnSurrogates != null);
Debug.Assert(dt.Columns.Count == _dataColumnSurrogates.Length);
for (int i = 0; i < dt.Columns.Count; i++) {
DataColumn dc = dt.Columns[i];
DataColumnSurrogate dataColumnSurrogate = _dataColumnSurrogates[i];
dataColumnSurrogate.SetColumnExpression(dc);
}
}
/*
Gets the records from the rows.
*/
private void GetRecords(DataRow row, int bitIndex) {
Debug.Assert(row != null);
ConvertToSurrogateRowState(row.RowState, bitIndex);
ConvertToSurrogateRecords(row, bitIndex);
ConvertToSurrogateRowError(row, bitIndex >> 1);
}
/*
Constructs the row, rowError and columnErrors.
*/
public DataRow ConvertToDataRow(DataTable dt, int bitIndex) {
DataRowState rowState = ConvertToRowState(bitIndex);
DataRow row = ConstructRow(dt, rowState, bitIndex);
ConvertToRowError(row, bitIndex >> 1);
return row;
}
/*
Sets the two bits in the bitArray to represent the DataRowState.
The 4 rowstates[Unchanged, Added, Modified, Deleted] are represented with 2 bits. The length of the BitArray will be twice the size of the number of rows.
Serialozed rowstate format : [00]->UnChanged, [01]->Added, [10]->Modified, [11]->Deleted.
*/
private void ConvertToSurrogateRowState(DataRowState rowState, int bitIndex) {
Debug.Assert(_rowStates != null);
Debug.Assert(_rowStates.Length > bitIndex);
switch (rowState) {
case DataRowState.Unchanged:
_rowStates[bitIndex] = false;
_rowStates[bitIndex + 1] = false;
break;
case DataRowState.Added:
_rowStates[bitIndex] = false;
_rowStates[bitIndex + 1] = true;
break;
case DataRowState.Modified:
_rowStates[bitIndex] = true;
_rowStates[bitIndex + 1] = false;
break;
case DataRowState.Deleted:
_rowStates[bitIndex] = true;
_rowStates[bitIndex + 1] = true;
break;
default:
throw new InvalidEnumArgumentException(String.Format("Unrecognized row state {0}", rowState));
}
}
/*
Constructs the RowState from the two bits in the bitarray.
Deserialize rowstate format : [00]->UnChanged, [01]->Added, [10]->Modified, [11]->Deleted.
*/
private DataRowState ConvertToRowState(int bitIndex) {
Debug.Assert(_rowStates != null);
Debug.Assert(_rowStates.Length > bitIndex);
bool b1 = _rowStates[bitIndex];
bool b2 = _rowStates[bitIndex + 1];
if (!b1 && !b2) {
return DataRowState.Unchanged;
} else if (!b1 && b2) {
return DataRowState.Added;
} else if (b1 && !b2) {
return DataRowState.Modified;
} else if (b1 && b2) {
return DataRowState.Deleted;
} else {
throw new ArgumentException("Unrecognized bitpattern");
}
}
/*
Constructs surrogate records from the DataRow.
*/
private void ConvertToSurrogateRecords(DataRow row, int bitIndex) {
Debug.Assert(row != null);
Debug.Assert(_records != null);
int colCount = row.Table.Columns.Count;
DataRowState rowState = row.RowState;
Debug.Assert(_records.Length == colCount);
if (rowState != DataRowState.Added) {//Unchanged, modified, deleted
for (int i = 0; i < colCount; i++) {
Debug.Assert(_records[i].Length > bitIndex);
_records[i][bitIndex] = row[i, DataRowVersion.Original];
}
}
if (rowState != DataRowState.Unchanged && rowState != DataRowState.Deleted) {//Added, modified state
for (int i = 0; i < colCount; i++) {
Debug.Assert(_records[i].Length > bitIndex + 1);
_records[i][bitIndex + 1] = row[i, DataRowVersion.Current];
}
}
}
/*
Constructs a DataRow from records[original and current] and adds the row to the DataTable rows collection.
*/
private DataRow ConstructRow(DataTable dt, DataRowState rowState, int bitIndex) {
Debug.Assert(dt != null);
Debug.Assert(_records != null);
DataRow row = dt.NewRow();
int colCount = dt.Columns.Count;
Debug.Assert(_records.Length == colCount);
switch (rowState) {
case DataRowState.Unchanged:
for (int i = 0; i < colCount; i++) {
Debug.Assert(_records[i].Length > bitIndex);
row[i] = _records[i][bitIndex]; //Original Record
}
dt.Rows.Add(row);
row.AcceptChanges();
break;
case DataRowState.Added:
for (int i = 0; i < colCount; i++) {
Debug.Assert(_records[i].Length > bitIndex + 1);
row[i] = _records[i][bitIndex + 1]; //Current Record
}
dt.Rows.Add(row);
break;
case DataRowState.Modified:
for (int i = 0; i < colCount; i++) {
Debug.Assert(_records[i].Length > bitIndex);
row[i] = _records[i][bitIndex]; //Original Record
}
dt.Rows.Add(row);
row.AcceptChanges();
row.BeginEdit();
for (int i = 0; i < colCount; i++) {
Debug.Assert(_records[i].Length > bitIndex + 1);
row[i] = _records[i][bitIndex + 1]; //Current Record
}
row.EndEdit();
break;
case DataRowState.Deleted:
for (int i = 0; i < colCount; i++) {
Debug.Assert(_records[i].Length > bitIndex);
row[i] = _records[i][bitIndex]; //Original Record
}
dt.Rows.Add(row);
row.AcceptChanges();
row.Delete();
break;
default:
throw new InvalidEnumArgumentException(String.Format("Unrecognized row state {0}", rowState));
}
return row;
}
/*
Constructs the surrogate rowerror, columnsInError and columnErrors.
*/
private void ConvertToSurrogateRowError(DataRow row, int rowIndex) {
Debug.Assert(row != null);
Debug.Assert(_rowErrors != null);
Debug.Assert(_colErrors != null);
if (row.HasErrors) {
_rowErrors.Add(rowIndex, row.RowError);
DataColumn[] dcArr = row.GetColumnsInError();
if (dcArr.Length > 0) {
int[] columnsInError = new int[dcArr.Length];
string[] columnErrors = new string[dcArr.Length];
for (int i = 0; i < dcArr.Length; i++) {
columnsInError[i] = dcArr[i].Ordinal;
columnErrors[i] = row.GetColumnError(dcArr[i]);
}
ArrayList list = new ArrayList();
list.Add(columnsInError);
list.Add(columnErrors);
_colErrors.Add(rowIndex, list);
}
}
}
/*
Set the row and columns in error.
*/
private void ConvertToRowError(DataRow row, int rowIndex) {
Debug.Assert(row != null);
Debug.Assert(_rowErrors != null);
Debug.Assert(_colErrors != null);
if (_rowErrors.ContainsKey(rowIndex)) {
row.RowError = (string) _rowErrors[rowIndex];
}
if (_colErrors.ContainsKey(rowIndex)) {
ArrayList list = (ArrayList) _colErrors[rowIndex];
int[] columnsInError = (int[]) list[0];
string[] columnErrors = (string[]) list[1];
Debug.Assert(columnsInError.Length == columnErrors.Length);
for (int i = 0; i < columnsInError.Length; i++) {
row.SetColumnError(columnsInError[i], columnErrors[i]);
}
}
}
/*
Suppress the read-only property and returns an arraylist of read-only columns.
*/
private ArrayList SuppressReadOnly(DataTable dt) {
Debug.Assert(dt != null);
ArrayList readOnlyList = new ArrayList();
for (int j = 0; j < dt.Columns.Count; j++) {
if (dt.Columns[j].Expression == String.Empty && dt.Columns[j].ReadOnly == true) {
readOnlyList.Add(j);
}
}
return readOnlyList;
}
/*
Suppress the foreign key constraint rules and returns an arraylist of the existing foreignkey constraint rules.
*/
private ArrayList SuppressConstraintRules(DataTable dt) {
Debug.Assert(dt != null);
ArrayList constraintRulesList = new ArrayList();
DataSet ds = dt.DataSet;
if (ds != null) {
for (int i = 0; i < ds.Tables.Count; i++) {
DataTable dtChild = ds.Tables[i];
for (int j = 0; j < dtChild.Constraints.Count; j++) {
Constraint c = dtChild.Constraints[j];
if (c is ForeignKeyConstraint) {
ForeignKeyConstraint fk = (ForeignKeyConstraint) c;
if (fk.RelatedTable == dt) {
ArrayList list = new ArrayList();
list.Add(new int[] { i, j });
list.Add(new int[] { (int) fk.AcceptRejectRule, (int) fk.UpdateRule, (int) fk.DeleteRule });
constraintRulesList.Add(list);
fk.AcceptRejectRule = AcceptRejectRule.None;
fk.UpdateRule = Rule.None;
fk.DeleteRule = Rule.None;
}
}
}
}
}
return constraintRulesList;
}
/*
Resets the read-only columns on the datatable based on the input readOnly list.
*/
private void ResetReadOnly(DataTable dt, ArrayList readOnlyList) {
Debug.Assert(dt != null);
Debug.Assert(readOnlyList != null);
DataSet ds = dt.DataSet;
foreach (object o in readOnlyList) {
int columnIndex = (int) o;
Debug.Assert(dt.Columns.Count > columnIndex);
dt.Columns[columnIndex].ReadOnly = true;
}
}
/*
Reset the foreignkey constraint rules on the datatable based on the input constraintRules list.
*/
private void ResetConstraintRules(DataTable dt, ArrayList constraintRulesList) {
Debug.Assert(dt != null);
Debug.Assert(constraintRulesList != null);
DataSet ds = dt.DataSet;
foreach (ArrayList list in constraintRulesList) {
Debug.Assert(list.Count == 2);
int[] indicesArr = (int[]) list[0];
int[] rules = (int[]) list[1];
Debug.Assert(indicesArr.Length == 2);
int tableIndex = indicesArr[0];
int constraintIndex = indicesArr[1];
Debug.Assert(ds.Tables.Count > tableIndex);
DataTable dtChild = ds.Tables[tableIndex];
Debug.Assert(dtChild.Constraints.Count > constraintIndex);
ForeignKeyConstraint fk = (ForeignKeyConstraint) dtChild.Constraints[constraintIndex];
Debug.Assert(rules.Length == 3);
fk.AcceptRejectRule = (AcceptRejectRule) rules[0];
fk.UpdateRule = (Rule) rules[1];
fk.DeleteRule = (Rule) rules[2];
}
}
/*
Checks whether the datatable schema matches with the surrogate schema.
*/
private bool IsSchemaIdentical(DataTable dt) {
Debug.Assert(dt != null);
if (dt.TableName != _tableName || dt.Namespace != _namespace) {
return false;
}
Debug.Assert(_dataColumnSurrogates != null);
if (dt.Columns.Count != _dataColumnSurrogates.Length) {
return false;
}
for (int i = 0; i < dt.Columns.Count; i++) {
DataColumn dc = dt.Columns[i];
DataColumnSurrogate dataColumnSurrogate = _dataColumnSurrogates[i];
if (!dataColumnSurrogate.IsSchemaIdentical(dc)) {
return false;
}
}
return true;
}
}
[Serializable]
class DataColumnSurrogate {
private string _columnName;
private string _namespace;
private string _prefix;
private MappingType _columnMapping;
private bool _allowNull;
private bool _autoIncrement;
private long _autoIncrementStep;
private long _autoIncrementSeed;
private string _caption;
private object _defaultValue;
private bool _readOnly;
private int _maxLength;
private Type _dataType;
private string _expression;
//ExtendedProperties
private Hashtable _extendedProperties;
/*
Constructs a DataColumnSurrogate from a DataColumn.
*/
public DataColumnSurrogate(DataColumn dc) {
if (dc == null) {
throw new ArgumentNullException("The datacolumn parameter is null");
}
_columnName = dc.ColumnName;
_namespace = dc.Namespace;
_dataType = dc.DataType;
_prefix = dc.Prefix;
_columnMapping = dc.ColumnMapping;
_allowNull = dc.AllowDBNull;
_autoIncrement = dc.AutoIncrement;
_autoIncrementStep = dc.AutoIncrementStep;
_autoIncrementSeed = dc.AutoIncrementSeed;
_caption = dc.Caption;
_defaultValue = dc.DefaultValue;
_readOnly = dc.ReadOnly;
_maxLength = dc.MaxLength;
_expression = dc.Expression;
//ExtendedProperties
_extendedProperties = new Hashtable();
if (dc.ExtendedProperties.Keys.Count > 0) {
foreach (object propertyKey in dc.ExtendedProperties.Keys) {
_extendedProperties.Add(propertyKey, dc.ExtendedProperties[propertyKey]);
}
}
}
/*
Constructs a DataColumn from DataColumnSurrogate.
*/
public DataColumn ConvertToDataColumn() {
DataColumn dc = new DataColumn();
dc.ColumnName = _columnName;
dc.Namespace = _namespace;
dc.DataType = _dataType;
dc.Prefix = _prefix;
dc.ColumnMapping = _columnMapping;
dc.AllowDBNull = _allowNull;
dc.AutoIncrement = _autoIncrement;
dc.AutoIncrementStep = _autoIncrementStep;
dc.AutoIncrementSeed = _autoIncrementSeed;
dc.Caption = _caption;
dc.DefaultValue = _defaultValue;
dc.ReadOnly = _readOnly;
dc.MaxLength = _maxLength;
//dc.Expression = _expression;
//Extended properties
Debug.Assert(_extendedProperties != null);
if (_extendedProperties.Keys.Count > 0) {
foreach (object propertyKey in _extendedProperties.Keys) {
dc.ExtendedProperties.Add(propertyKey, _extendedProperties[propertyKey]);
}
}
return dc;
}
/*
Set expression on the DataColumn.
*/
internal void SetColumnExpression(DataColumn dc) {
Debug.Assert(dc != null);
if (_expression != null && !_expression.Equals(String.Empty)) {
dc.Expression = _expression;
}
}
/*
Checks whether the column schema is identical. Marked internal as the DataTableSurrogate objects needs to have access to this object.
Note: ReadOnly is not checked here as we suppress readonly when reading data.
*/
internal bool IsSchemaIdentical(DataColumn dc) {
Debug.Assert(dc != null);
if ((dc.ColumnName != _columnName) || (dc.Namespace != _namespace) || (dc.DataType != _dataType) ||
(dc.Prefix != _prefix) || (dc.ColumnMapping != _columnMapping) ||
(dc.ColumnMapping != _columnMapping) || (dc.AllowDBNull != _allowNull) ||
(dc.AutoIncrement != _autoIncrement) || (dc.AutoIncrementStep != _autoIncrementStep) ||
(dc.AutoIncrementSeed != _autoIncrementSeed) || (dc.Caption != _caption) ||
(!(AreDefaultValuesEqual(dc.DefaultValue, _defaultValue))) || (dc.MaxLength != _maxLength) ||
(dc.Expression != _expression)) {
return false;
}
return true;
}
/*
Checks whether the default boxed objects are equal.
*/
internal static bool AreDefaultValuesEqual(object o1, object o2) {
if (o1 == null && o2 == null) {
return true;
} else if (o1 == null || o2 == null) {
return false;
} else {
return o1.Equals(o2);
}
}
}