Click here to Skip to main content
15,895,656 members
Articles / Programming Languages / C#

Optimizing Serialization in .NET - part 2

Rate me:
Please Sign up or sign in to vote.
4.95/5 (34 votes)
27 Nov 2006Public Domain33 min read 328.5K   3.7K   156  
Provides code and techniques to enable developers to optimize serialization of DataSets/DataTables.
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Data;
using System.Globalization;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using SimmoTech.Utils.Serialization;

namespace SimmoTech.Utils.Data
{
	
	/// <summary>
	/// Contains static helper methods for ADO.Net objects including Fast Serialization
	/// </summary>
	public class AdoNetHelper
	{
		
		#region Constructors
		private AdoNetHelper() {}
		#endregion Constructors
		
		#region Private
		private static readonly int[] zeroIntegers = new int[0];
		#endregion Private
		
		#region Static Methods
		/// <summary>
		/// Serializes a Typed DataTable to a byte[] containing only the data
		/// (ie no infrastructure)
		/// 
		/// The DataTable must be Typed and not a plain DataTable. It must also not have had any 
		/// new columns added to it. In either of these cases, use SerializeDataTable instead.
		/// </summary>
		/// <param name="dataTable">The Typed DataTable to serialize.</param>
		/// <returns>A byte[] containing the serialized data.</returns>
		public static byte[] SerializeTypedDataTable(DataTable dataTable)
		{
			if (dataTable == null) throw new ArgumentNullException("dataTable");
			if (dataTable.GetType() == typeof(DataTable)) throw new ArgumentException("Is not a typed DataTable", "dataTable");
			if (dataTable.GetType().GetConstructor(Type.EmptyTypes) == null) throw new ArgumentException("Does not have a public, empty constructor", "dataTable");
			
			return new FastSerializer().SerializeDataOnly(dataTable);
		}
		
		/// <summary>
		/// Serializes a Typed DataSet to a byte[] containing only the data for each DataTable
		/// (ie no infrastructure)
		/// 
		/// The DataSet must be Typed and not a plain DataSet. It must also not have had any 
		/// new columns/tables added to it. In either of these cases, use SerializeDataSet instead.
		/// </summary>
		/// <param name="dataSet">The Typed DataSet to serialize.</param>
		/// <returns>A byte[] containing the serialized data.</returns>
		public static byte[] SerializeTypedDataSet(DataSet dataSet)
		{
			if (dataSet == null) throw new ArgumentNullException("dataSet");
			if (dataSet.GetType() == typeof(DataSet)) throw new ArgumentException("Is not a typed DataSet", "dataSet");

			return new FastSerializer().SerializeDataOnly(dataSet);
		}

		/// <summary>
		/// Serializes a DataSet to a byte[].
		/// </summary>
		/// <param name="dataSet">The DataSet to serialize.</param>
		/// <returns>A byte[] containing the serialized data.</returns>
		public static byte[] SerializeDataSet(DataSet dataSet)
		{
			if (dataSet == null) throw new ArgumentNullException("dataSet");

			return new FastSerializer().Serialize(dataSet);
		}
		
		/// <summary>
		/// Serializes a DataTable to a byte[].
		/// </summary>
		/// <param name="dataTable">The DataTable to serialize.</param>
		/// <returns>A byte[] containing the serialized data.</returns>
		public static byte[] SerializeDataTable(DataTable dataTable)
		{
			if (dataTable == null) throw new ArgumentNullException("dataTable");
			
			return new FastSerializer().Serialize(dataTable);
		}
		
		/// <summary>
		/// Serializes a simple Typed DataTable to a byte[] containing only data.
		/// 
		/// A simple Typed DataTable will have no Errors associated with the rows
		/// or columns and all rows should be Unchanged/Added (deserialized
		/// rows will always be Unchanged).  Deleted rows will throw an exception.
		/// Designed for read-only tables which need to be serialized to a minumum size.
		/// 
		/// </summary>
		/// <param name="dataTable">The Typed DataTable to serialize.</param>
		/// <returns>A byte[] containing the serialized data.</returns>
		public static byte[] SerializeSimpleTypedDataTable(DataTable dataTable)
		{
			if (dataTable == null) throw new ArgumentNullException("dataTable");
			if (dataTable.HasErrors) throw new ArgumentException("Table has errors so is not a simple table", "dataTable");
			if (dataTable.GetType().GetConstructor(Type.EmptyTypes) == null) throw new ArgumentException("Does not have a public, empty constructor", "dataTable");
			
			return new FastSerializer().SerializeSimpleDataOnly(dataTable);
		}
		
		/// <summary>
		/// Deserializes a Typed DataSet from a byte[] containing serialized data only.
		/// 
		/// The Type must match that from which the serialized data was originally obtained
		/// and it must have a parameterless constructor.
		/// </summary>
		/// <param name="dataSetType">The Type of Typed DataSet to deserialize.</param>
		/// <param name="serializedData">A byte[] containing the serialized data.</param>
		/// <returns>A new DataSet of the requested type.</returns>
		public static DataSet DeserializeTypedDataSet(Type dataSetType, byte[] serializedData)
		{
			if (dataSetType == null) throw new ArgumentNullException("dataSetType");
			if (serializedData == null) throw new ArgumentNullException("serializedData");
			
			DataSet dataSet = Activator.CreateInstance(dataSetType) as DataSet;
			if(dataSet == null || dataSet.GetType() == typeof(DataSet)) {
				throw new ArgumentException("Type is not a typed DataSet", "dataSetType");
			}
			
			return new FastDeserializer().DeserializeDataSetDataOnly(dataSet, serializedData);
		}
		
		/// <summary>
		/// Deserializes a Typed DataSet from a byte[] containing serialized data only.
		/// 
		/// The DataSet must be of the same type from which the serialized data was originally obtained.
		/// </summary>
		/// <param name="dataSet">The Typed DataSet to deserialize into.</param>
		/// <param name="serializedData">A byte[] containing the serialized data.</param>
		/// <returns>The same DataSet passed in.</returns>
		public static DataSet DeserializeTypedDataSet(DataSet dataSet, byte[] serializedData)
		{
			if (dataSet == null) throw new ArgumentNullException("dataSet");
			if (serializedData == null) throw new ArgumentNullException("serializedData");
			if (dataSet.GetType() == typeof(DataSet)) throw new ArgumentException("Is not a typed DataSet", "dataSet");
			
			return new FastDeserializer().DeserializeDataSetDataOnly(dataSet, serializedData);
		}
		
		/// <summary>
		/// Deserializes a Typed DataTable from a byte[] containing serialized data only.
		/// 
		/// The Type must match that from which the serialized data was originally obtained
		/// and it must have a parameterless constructor.
		/// </summary>
		/// <param name="dataTableType">The Type of Typed DataTable to deserialize.</param>
		/// <param name="serializedData">A byte[] containing the serialized data.</param>
		/// <returns>A new DataTable of the requested type.</returns>
		public static DataTable DeserializeTypedDataTable(Type dataTableType, byte[] serializedData)
		{
			if (dataTableType == null) throw new ArgumentNullException("dataTableType");
			if (serializedData == null) throw new ArgumentNullException("serializedData");
			if (dataTableType.GetConstructor(Type.EmptyTypes) == null) throw new ArgumentException("Does not have a public, empty constructor", "dataTable");
			
			DataTable DataTable = Activator.CreateInstance(dataTableType) as DataTable;
			if (DataTable == null || DataTable.GetType() == typeof(DataTable))
			{
				throw new ArgumentException("Type is not a typed DataTable", "dataTableType");
			}
			
			return new FastDeserializer().DeserializeDataTableDataOnly(DataTable, serializedData);
		}
		
		/// <summary>
		/// Deserializes a Typed DataTable from a byte[] containing serialized data only.
		/// 
		/// The DataTable must be of the same type from which the serialized data was originally obtained.
		/// </summary>
		/// <param name="dataTable">The Typed DataTable to deserialize into.</param>
		/// <param name="serializedData">A byte[] containing the serialized data.</param>
		/// <returns>The same DataTable passed in.</returns>
		public static DataTable DeserializeTypedDataTable(DataTable dataTable, byte[] serializedData)
		{
			if (dataTable == null) throw new ArgumentNullException("dataTable");
			if (serializedData == null) throw new ArgumentNullException("serializedData");
			if (dataTable.GetType() == typeof(DataTable)) throw new ArgumentException("Is not a typed DataTable", "dataTable");
			
			return new FastDeserializer().DeserializeDataTableDataOnly(dataTable, serializedData);
		}
		
		/// <summary>
		/// Deserializes a DataSet or Typed DataSet from a byte[].
		/// 
		/// The Type must match that from which the serialized data was originally obtained
		/// and it must have a parameterless constructor.
		/// </summary>
		/// <param name="dataSetType">The Type of DataSet to deserialize.</param>
		/// <param name="serializedData">A byte[] containing the serialized data.</param>
		/// <returns></returns>
		public static DataSet DeserializeDataSet(Type dataSetType, byte[] serializedData)
		{
			if (dataSetType == null) throw new ArgumentNullException("dataSetType");
			if (serializedData == null) throw new ArgumentNullException("serializedData");
			
			DataSet dataSet = Activator.CreateInstance(dataSetType) as DataSet;
			if (dataSet == null) throw new ArgumentException("Type is not a DataSet", "dataSetType");
			
			return new FastDeserializer().DeserializeDataSetDataOnly(dataSet, serializedData);
		}
		
		/// <summary>
		/// Creates a new DataSet and populates it from originally serialized data.
		/// </summary>
		/// <param name="serializedData">A byte[] containing the serialized data.</param>
		/// <returns>A new and populated DataSet.</returns>
		public static DataSet DeserializeDataSet(byte[] serializedData)
		{
			if (serializedData == null) throw new ArgumentNullException("serializedData");
			
			return new FastDeserializer().DeserializeDataSet(new DataSet(), serializedData);
		}

		/// <summary>
		/// Deserializes data and infrastructure into the supplied DataSet.
		/// </summary>
		/// <param name="dataSet">The DataSet to populate.</param>
		/// <param name="serializedData">A byte[] containing the serialized data.</param>
		/// <returns>The same DataSet passed in.</returns>
		public static DataSet DeserializeDataSet(DataSet dataSet, byte[] serializedData)
		{
			if (dataSet == null) throw new ArgumentNullException("dataSet");
			if (serializedData == null) throw new ArgumentNullException("serializedData");
			
			return new FastDeserializer().DeserializeDataSet(dataSet, serializedData);
		}
		
		/// <summary>
		/// Creates a new DataTable and populates it with previously serialized data.
		/// </summary>
		/// <param name="serializedData">A byte[] containing the serialized data.</param>
		/// <returns>A new and populated DataTable</returns>
		public static DataTable DeserializeDataTable(byte[] serializedData)
		{
			return DeserializeDataTable(new DataTable(), serializedData);
		}
		
		/// <summary>
		/// Populates the supplied DataTable with previously serialized data.
		/// </summary>
		/// <param name="dataTable">The DataTable to populate.</param>
		/// <param name="serializedData">A byte[] containing the serialized data.</param>
		/// <returns>The same DataTable passed in.</returns>
		public static DataTable DeserializeDataTable(DataTable dataTable, byte[] serializedData)
		{
			if (dataTable == null) throw new ArgumentNullException("dataTable");
			if (serializedData == null) throw new ArgumentNullException("serializedData");

			return new FastDeserializer().DeserializeDataTable(dataTable, serializedData);
		}
		
		/// <summary>
		/// Populates an existing Typed DataTable with row data previously serialized using
		/// SerializeSimpleTypedDataTable.
		/// 
		/// All rows will have Unchanged state even if they were Added or Modified at
		/// the time of serialization.
		/// </summary>
		/// <param name="dataTable">The DataTable into which to deserialize row data.</param>
		/// <param name="serializedData">A byte[] containing the serialized data.</param>
		/// <returns>The same DataTable passed in.</returns>
		public static DataTable DeserializeSimpleTypedDataTable(DataTable dataTable, byte[] serializedData)
		{
			if (dataTable == null) throw new ArgumentNullException("dataTable");
			if (serializedData == null) throw new ArgumentNullException("serializedData");
			
			return new FastDeserializer().DeserializeSimpleTypedDataTable(dataTable, serializedData);
		}
		
		/// <summary>
		/// Create a new DataTable of the requested type and populates it with row data previously
		/// serialized using SerializeSimpleDataTable.
		/// 
		/// The type must have a parameterless constuctor and should be of the same type as
		/// previously serialized.
		/// 
		/// All rows will have Unchanged state even if they were Added or Modified at
		/// the time of serialization.
		/// </summary>
		/// <param name="dataTableType">The Type of DataTable to create.</param>
		/// <param name="serializedData">A byte[] containing the serialized data.</param>
		/// <returns></returns>
		public static DataTable DeserializeSimpleTypedDataTable(Type dataTableType, byte[] serializedData)
		{
			if (dataTableType == null) throw new ArgumentNullException("dataTableType");
			if (serializedData == null) throw new ArgumentNullException("serializedData");
			if (dataTableType.GetConstructor(Type.EmptyTypes) == null) throw new ArgumentException("Does not have a public, empty constructor", "dataTable");
			
			DataTable dataTable = Activator.CreateInstance(dataTableType) as DataTable;
			if (dataTable == null) throw new ArgumentException("Type is not a DataTable", "dataTableType");
			
			return new FastDeserializer().DeserializeSimpleTypedDataTable(dataTable, serializedData);
		}

		/// <summary>
		/// Returns an object[] of all the row values for the specified version
		/// </summary>
		/// <param name="row">The row from which to extract values</param>
		/// <param name="version"></param>
		/// <returns>An object[] holding the values.</returns>
		public static object[] GetValuesFromRowVersion(DataRow row, DataRowVersion version)
		{
			int columnCount = row.Table.Columns.Count;
			object[] result = new object[columnCount];
			for(int j = 0; j < columnCount; j++)
			{
				result[j] = row[j, version];
			}
			return result;
		}

		/// <summary>
		/// Maps DateTime.MinValue to DBNull.Value.
		/// </summary>
		/// <param name="value">The DateTime value to check.</param>
		/// <returns>DBNull.Value if the DateTime is MinValue; otherwise the DateTime value.</returns>
		public static object GetNullableDateTime(DateTime value) {
			if (value == DateTime.MinValue)
				return DBNull.Value;
			else {
				return value;
			}
		}
		
		/// <summary>
		/// Gets an object[] holding the PrimaryKey column values.
		/// The Row must be in a DataTable.
		/// </summary>
		/// <param name="row">The row from which to find values.</param>
		/// <returns>An object[] holding the PrimaryKey values for the row, or an empty object[] if there is no PrimaryKey defined.</returns>
		public static object[] GetKeyValuesFromRow(DataRow row) {
			if (row.Table == null) throw new ArgumentOutOfRangeException("row", "Row is not in a table");
			return GetKeyValuesFromRow(row, row.Table);
		}

		/// <summary>
		/// Gets an object[] holding the values from the columns having the same names as those of the PrimaryKey columns
		/// from the supplied DataTable.
		/// </summary>
		/// <param name="row">The row from which to find values.</param>
		/// <param name="primaryKeyProvider">The DataTable holding the PrimaryKey columns.</param>
		/// <returns>An object[] holding the corresponding values for the row, or an empty object[] if there is no PrimaryKey defined.</returns>
		public static object[] GetKeyValuesFromRow(DataRow row, DataTable primaryKeyProvider) {
			if (row == null) throw new ArgumentNullException("row");
			if (primaryKeyProvider == null) throw new ArgumentNullException("primaryKeyProvider");

			DataColumn[] keyColumns = primaryKeyProvider.PrimaryKey;
			DataRowVersion rowVersion = row.RowState == DataRowState.Deleted ? DataRowVersion.Original : DataRowVersion.Current;

			object[] keyValues = new object[keyColumns.Length];
			for(int i = 0; i < keyColumns.Length; i++) {
				keyValues[i] = row[keyColumns[i].ColumnName, rowVersion];
			}

			return keyValues;
		}
		
		/// <summary>
		/// Gets a list of unique values for a column from a list of rows.
		/// </summary>
		/// <param name="rows">The DataRows in which to look for unique values.</param>
		/// <param name="columnName">The name of the column in which to look for unique values.</param>
		/// <returns>An ArrayList containing the unique values.</returns>
		public static ArrayList GetUniqueColumnValues(DataRow[] rows, string columnName) {
			if (rows == null) throw new ArgumentNullException();
			if (columnName == null) throw new ArgumentNullException();
			if (rows.Length == 0) return new ArrayList();
			DataColumn column = rows[0].Table.Columns[columnName];
			if (column == null) throw new DataException(string.Format("Column '{0}' is not in Table '{1}'", columnName, rows[0].Table.TableName));

			return getUniqueValues(rows, column);
		}

		/// <summary>
		/// Gets a list of unique values for a column from a list of rows.
		/// </summary>
		/// <param name="rows">The DataRows in which to look for unique values.</param>
		/// <param name="columnOrdinal">The ordinal of the column in which to look for unique values.</param>
		/// <returns>An ArrayList containing the unique values.</returns>
		public static ArrayList GetUniqueColumnValues(DataRow[] rows, int columnOrdinal) {
			if (rows == null) throw new ArgumentNullException();
			DataColumn column = rows[0].Table.Columns[columnOrdinal];

			return getUniqueValues(rows, column);
		}

		/// <summary>
		/// Gets a list of unique values for a column from a list of rows.
		/// </summary>
		/// <param name="rows">The DataRows in which to look for unique values.</param>
		/// <param name="column">The column in which to look for unique values.</param>
		/// <returns>An ArrayList containing the unique values.</returns>
		public static ArrayList GetUniqueColumnValues(DataRow[] rows, DataColumn column) {
			if (rows == null) throw new ArgumentNullException();
			if (column == null) throw new ArgumentNullException();
			
			return getUniqueValues(rows, column);
		}

		/// <summary>
		/// Create a simple string dump of the contents (each table) of a DataSet.
		/// 
		/// Includes the key values for each row.
		/// </summary>
		/// <param name="dataset">The DataSet to dump</param>
		/// <param name="description">A description of the DataSet contents</param>
		/// <returns></returns>
		public static string GetDataSetDump(DataSet dataset, string description) {
			StringBuilder sb = new StringBuilder();

			// Dump the description
			sb.Append(description);
			sb.Append(Environment.NewLine);

			// Dump each contained table
			foreach(DataTable dataTable in dataset.Tables) {
				sb.Append(getTableDump(dataTable));
			}

			// Return the dump string
			return sb.ToString();
			
		}

		public static string GetTableDump(DataTable dataTable, string description) {
			StringBuilder sb = new StringBuilder();

			// Dump the description
			sb.Append(description);
			sb.Append(Environment.NewLine);

			// Dump the table
			sb.Append(getTableDump(dataTable));

			// Return the dump string
			return sb.ToString();
			
		}
		#endregion Static Methods
		
		#region Private Static Methods
		private static string getTableDump(DataTable dataTable) {
			StringBuilder sb = new StringBuilder();

			sb.AppendFormat("table '{0}' contains {1} rows", dataTable.TableName, dataTable.Rows.Count);
			sb.Append(Environment.NewLine);
			int rowNumber = 0;
			foreach(DataRow row in dataTable.Rows) {
				object[] keyValues = AdoNetHelper.GetKeyValuesFromRow(row);
				string[] keyValueStrings = new string[keyValues.Length];
				for(int i = 0; i < keyValues.Length; i++) {
					keyValueStrings[i] = keyValues[i].ToString();
				}

				sb.AppendFormat("{0,3}: State={1}, key={2}", rowNumber, row.RowState, string.Join(";", keyValueStrings));
				sb.Append(Environment.NewLine);

				rowNumber++;
			}
			sb.Append(Environment.NewLine);
			sb.Append(Environment.NewLine);

			return sb.ToString();
		}

		private static ArrayList getUniqueValues(DataRow[] rows, DataColumn column) {
			ArrayList uniqueValues = new ArrayList();

			if (rows.Length == 1)
				uniqueValues.Add(rows[0][column]);
			else if (rows.Length != 0) {
				foreach(DataRow row in rows) {
					object value = row[column];
					if (!uniqueValues.Contains(value)) {
						uniqueValues.Add(value);
					}
				}
			}

			return uniqueValues;
		}
		#endregion Private Static Methods
		
		#region Fast Serialization
		#region Flags
		#region DataSet
		private static readonly int DataSetHasName = BitVector32.CreateMask();
		private static readonly int DataSetIsCaseSensitive = BitVector32.CreateMask(DataSetHasName);
		private static readonly int DataSetHasTables = BitVector32.CreateMask(DataSetIsCaseSensitive);
		private static readonly int DataSetHasRelationships = BitVector32.CreateMask(DataSetHasTables);
		private static readonly int DataSetHasForeignKeyConstraints = BitVector32.CreateMask(DataSetHasRelationships);
		private static readonly int DataSetHasExtendedProperties = BitVector32.CreateMask(DataSetHasForeignKeyConstraints);
		private static readonly int DataSetAreConstraintsEnabled = BitVector32.CreateMask(DataSetHasExtendedProperties);
		private static readonly int DataSetHasNamespace = BitVector32.CreateMask(DataSetAreConstraintsEnabled);
		private static readonly int DataSetHasPrefix = BitVector32.CreateMask(DataSetHasNamespace);

		private static BitVector32 GetDataSetFlags(DataSet dataSet)
		{
			BitVector32 flags = new BitVector32();
			
			flags[DataSetHasName] = dataSet.DataSetName != "NewDataSet";
			flags[DataSetIsCaseSensitive] = dataSet.CaseSensitive;
			flags[DataSetHasTables] = dataSet.Tables.Count != 0;
			flags[DataSetHasRelationships] = dataSet.Relations.Count != 0;
			flags[DataSetHasForeignKeyConstraints] = getForeignKeyConstraints(dataSet).Length != 0;
			flags[DataSetHasExtendedProperties] = dataSet.ExtendedProperties.Count != 0;
			flags[DataSetAreConstraintsEnabled] = dataSet.EnforceConstraints;
			flags[DataSetHasNamespace] = dataSet.Namespace != string.Empty;
			flags[DataSetHasPrefix] = dataSet.Prefix != string.Empty;
			
			return flags;
		}
		#endregion DataSet

		#region Table
		private static readonly int TableHasName = BitVector32.CreateMask();
		private static readonly int TableIsCaseSensitive = BitVector32.CreateMask(TableHasName);
		private static readonly int TableIsCaseSensitiveAmbient = BitVector32.CreateMask(TableIsCaseSensitive);
		private static readonly int TableHasSpecificCulture = BitVector32.CreateMask(TableIsCaseSensitiveAmbient);
		private static readonly int TableHasColumns = BitVector32.CreateMask(TableHasSpecificCulture);
		private static readonly int TableHasRows = BitVector32.CreateMask(TableHasColumns);
		private static readonly int TableHasMinimumCapacity = BitVector32.CreateMask(TableHasRows);
		private static readonly int TableHasDisplayExpression = BitVector32.CreateMask(TableHasMinimumCapacity);
		private static readonly int TableHasNoUniqueConstraints = BitVector32.CreateMask(TableHasDisplayExpression);
		private static readonly int TableHasExtendedProperties = BitVector32.CreateMask(TableHasNoUniqueConstraints);
		private static readonly int TableHasNamespace = BitVector32.CreateMask(TableHasExtendedProperties);
		private static readonly int TableHasPrefix = BitVector32.CreateMask(TableHasNamespace);
		private static readonly int TableHasRepeatableElement = BitVector32.CreateMask(TableHasPrefix);
		private static readonly int TableHasTypeName = BitVector32.CreateMask(TableHasRepeatableElement);

		private static readonly FieldInfo TableCultureFieldInfo =
			typeof(DataTable).GetField("culture", BindingFlags.Instance | BindingFlags.NonPublic);
		private static readonly FieldInfo TableCaseSensitiveAmbientFieldInfo =
			typeof(DataTable).GetField("caseSensitiveAmbient", BindingFlags.Instance | BindingFlags.NonPublic);
		private static readonly FieldInfo TableTypeNameFieldInfo =
			typeof(DataTable).GetField("typeName", BindingFlags.Instance | BindingFlags.NonPublic);
		private static readonly FieldInfo TableRepeatableElementFieldInfo =
			typeof(DataTable).GetField("repeatableElement", BindingFlags.Instance | BindingFlags.NonPublic);
		
		private static BitVector32 GetTableFlags(DataTable dataTable)
		{
			BitVector32 flags = new BitVector32();
			
			flags[TableHasName] = dataTable.TableName != string.Empty;
			flags[TableIsCaseSensitive] = dataTable.CaseSensitive;
			flags[TableIsCaseSensitiveAmbient] = (bool) TableCaseSensitiveAmbientFieldInfo.GetValue(dataTable);
			flags[TableHasTypeName] = (XmlQualifiedName) TableTypeNameFieldInfo.GetValue(dataTable) != XmlQualifiedName.Empty;
			flags[TableHasSpecificCulture] = TableCultureFieldInfo.GetValue(dataTable) != null;
			flags[TableHasColumns] = dataTable.Columns.Count != 0;
			flags[TableHasRows] = dataTable.Rows.Count != 0;
			flags[TableHasMinimumCapacity] = dataTable.MinimumCapacity != 50;
			flags[TableHasDisplayExpression] = dataTable.DisplayExpression != string.Empty;
			flags[TableHasNoUniqueConstraints] = getUniqueConstraints(dataTable).Length == 0;
			flags[TableHasExtendedProperties] = dataTable.ExtendedProperties.Count != 0;
			
			flags[TableHasNamespace] = dataTable.Namespace != string.Empty;
			flags[TableHasPrefix] = dataTable.Prefix != string.Empty;
			flags[TableHasRepeatableElement] = (bool) TableRepeatableElementFieldInfo.GetValue(dataTable);
			
			return flags;
		}
		#endregion Table

		#region Column
		private static readonly int ColumnIsNullable = BitVector32.CreateMask();
		private static readonly int ColumnIsNotStringDataType = BitVector32.CreateMask(ColumnIsNullable);
		private static readonly int ColumnHasCaption = BitVector32.CreateMask(ColumnIsNotStringDataType);
		private static readonly int ColumnHasMaxLength = BitVector32.CreateMask(ColumnHasCaption);
		private static readonly int ColumnHasAutoIncrement = BitVector32.CreateMask(ColumnHasMaxLength);
		private static readonly int ColumnHasAutoIncrementUnusedDefaults = BitVector32.CreateMask(ColumnHasAutoIncrement);
		private static readonly int ColumnHasAutoIncrementNegativeStep = BitVector32.CreateMask(ColumnHasAutoIncrementUnusedDefaults);

		private static readonly int ColumnHasDefaultValue = BitVector32.CreateMask(ColumnHasAutoIncrementNegativeStep);
		private static readonly int ColumnHasExpression = BitVector32.CreateMask(ColumnHasDefaultValue);
		private static readonly int ColumnIsReadOnly = BitVector32.CreateMask(ColumnHasExpression);
		private static readonly int ColumnHasExtendedProperties = BitVector32.CreateMask(ColumnIsReadOnly);
		
		private static readonly int ColumnHasPrefix = BitVector32.CreateMask(ColumnHasExtendedProperties);
		private static readonly int ColumnIsNotElementMappingType = BitVector32.CreateMask(ColumnHasPrefix);
		private static readonly int ColumnHasColumnUri = BitVector32.CreateMask(ColumnIsNotElementMappingType);

		private static readonly FieldInfo ColumnUriFieldInfo =
			typeof(DataColumn).GetField("_columnUri", BindingFlags.Instance | BindingFlags.NonPublic);
		private static readonly FieldInfo AutoIncrementCurrentFieldInfo =
			typeof(DataColumn).GetField("autoIncrementCurrent", BindingFlags.Instance | BindingFlags.NonPublic);

		private static BitVector32 GetColumnFlags(DataColumn dataColumn)
		{
			BitVector32 flags = new BitVector32();

			flags[ColumnIsNullable] = dataColumn.AllowDBNull;
			flags[ColumnIsNotStringDataType] = dataColumn.DataType != typeof(string);
			flags[ColumnHasCaption] = dataColumn.Caption != dataColumn.ColumnName;
			flags[ColumnHasMaxLength] = dataColumn.MaxLength != -1;
			flags[ColumnHasAutoIncrement] = dataColumn.AutoIncrement;
			long current = (long) AutoIncrementCurrentFieldInfo.GetValue(dataColumn);
			long step = dataColumn.AutoIncrementStep;
			flags[ColumnHasAutoIncrementUnusedDefaults] = (current == 0 && step == 1) || (current == -1 && step == -1);
			flags[ColumnHasAutoIncrementNegativeStep] = dataColumn.AutoIncrementStep < 0;
			flags[ColumnHasDefaultValue] = dataColumn.DefaultValue != DBNull.Value;
			flags[ColumnHasExpression] = dataColumn.Expression != string.Empty;
			flags[ColumnIsReadOnly] = dataColumn.ReadOnly;
			flags[ColumnHasExtendedProperties] = dataColumn.ExtendedProperties.Count != 0;
			
			flags[ColumnHasPrefix] = dataColumn.Prefix != string.Empty;
			flags[ColumnIsNotElementMappingType] = dataColumn.ColumnMapping != MappingType.Element;
			flags[ColumnHasColumnUri] = ColumnUriFieldInfo.GetValue(dataColumn) != null;
			
			return flags;
		}
		#endregion Column
		
		#region Row
		private static readonly int RowHasOldData = BitVector32.CreateMask();
		private static readonly int RowHasNewData = BitVector32.CreateMask(RowHasOldData);
		private static readonly int RowHasRowError = BitVector32.CreateMask(RowHasNewData);
		private static readonly int RowHasColumnErrors = BitVector32.CreateMask(RowHasRowError);
		
		private static BitVector32 GetRowFlags(DataRow row)
		{
			BitVector32 flags = new BitVector32();
			
			DataRowState state = row.RowState;
			flags[RowHasOldData] = state == DataRowState.Deleted || state == DataRowState.Modified;
			flags[RowHasNewData] = state == DataRowState.Added || state == DataRowState.Modified;
			flags[RowHasRowError] = row.RowError.Length != 0;
			flags[RowHasColumnErrors] = row.GetColumnsInError().Length != 0;
			
			return flags;
		}
		#endregion Row
		
		#region Constraints
		#region Common
		private static readonly FieldInfo ConstraintSchemaNameFieldInfo =
			typeof(Constraint).GetField("_schemaName", BindingFlags.Instance | BindingFlags.NonPublic);
		private static readonly Regex DefaultConstraintNameMatcher = new Regex(@"^Constraint(\d+)$");
		#endregion Common
		
		#region Unique
		private static readonly int UniqueConstraintHasDefaultName = BitVector32.CreateMask();
		private static readonly int UniqueConstraintHasSchemaName = BitVector32.CreateMask(UniqueConstraintHasDefaultName);
		private static readonly int UniqueConstraintHasMultipleColumns = BitVector32.CreateMask(UniqueConstraintHasSchemaName);
		private static readonly int UniqueConstraintHasExtendedProperties = BitVector32.CreateMask(UniqueConstraintHasMultipleColumns);
		private static readonly int UniqueConstraintIsPrimaryKey = BitVector32.CreateMask(UniqueConstraintHasExtendedProperties);

		private static BitVector32 GetUniqueConstraintFlags(UniqueConstraint uniqueConstraint)
		{
			BitVector32 flags = new BitVector32();

			flags[UniqueConstraintHasDefaultName] = DefaultConstraintNameMatcher.IsMatch(uniqueConstraint.ConstraintName);
			
			flags[UniqueConstraintHasSchemaName] = (string) ConstraintSchemaNameFieldInfo.GetValue(uniqueConstraint) != string.Empty;
			flags[UniqueConstraintHasMultipleColumns] = uniqueConstraint.Columns.Length > 1;
			flags[UniqueConstraintHasExtendedProperties] = uniqueConstraint.ExtendedProperties.Count != 0;
			flags[UniqueConstraintIsPrimaryKey] = uniqueConstraint.IsPrimaryKey;
			
			return flags;
		}
		
		private static UniqueConstraint[] getUniqueConstraints(DataTable dataTable)
		{
			if (dataTable.Constraints.Count == 0) return new UniqueConstraint[0];
			ArrayList constraints = new ArrayList();
			foreach(Constraint constraint in dataTable.Constraints)
			{
				if (constraint is UniqueConstraint) constraints.Add(constraint);
			}
			
			return (UniqueConstraint[]) constraints.ToArray(typeof(UniqueConstraint));
		}
		#endregion Unique
		
		#region Foreign Key
		private static readonly int ForeignKeyConstraintHasDefaultName = BitVector32.CreateMask();
		private static readonly int ForeignKeyConstraintIsPrimaryKeyOnParentTable =
			BitVector32.CreateMask(ForeignKeyConstraintHasDefaultName);
		private static readonly int ForeignKeyConstraintHasMultipleColumns =
			BitVector32.CreateMask(ForeignKeyConstraintIsPrimaryKeyOnParentTable);
		private static readonly int ForeignKeyConstraintHasAcceptRejectRule = BitVector32.CreateMask(ForeignKeyConstraintHasMultipleColumns);
		private static readonly int ForeignKeyConstraintHasNonCascadeDeleteRule =
			BitVector32.CreateMask(ForeignKeyConstraintHasAcceptRejectRule);
		private static readonly int ForeignKeyConstraintHasNonCascadeUpdateRule =
			BitVector32.CreateMask(ForeignKeyConstraintHasNonCascadeDeleteRule);
		private static readonly int ForeignKeyConstraintHasExtendedProperties =
			BitVector32.CreateMask(ForeignKeyConstraintHasNonCascadeUpdateRule);
		private static readonly int ForeignKeyConstraintHasSchemaName = BitVector32.CreateMask(ForeignKeyConstraintHasExtendedProperties);
		
		private static BitVector32 GetForeignKeyConstraintFlags(ForeignKeyConstraint foreignKeyConstraint)
		{
			BitVector32 flags = new BitVector32();

			flags[ForeignKeyConstraintHasDefaultName] = DefaultConstraintNameMatcher.IsMatch(foreignKeyConstraint.ConstraintName);
			flags[ForeignKeyConstraintIsPrimaryKeyOnParentTable] =
				ColumnOrdinalsMatch(foreignKeyConstraint.RelatedColumns, foreignKeyConstraint.RelatedTable.PrimaryKey);
			flags[ForeignKeyConstraintHasMultipleColumns] = foreignKeyConstraint.Columns.Length > 1;
			flags[ForeignKeyConstraintHasAcceptRejectRule] = foreignKeyConstraint.AcceptRejectRule != AcceptRejectRule.None;
			flags[ForeignKeyConstraintHasNonCascadeUpdateRule] = foreignKeyConstraint.UpdateRule != Rule.Cascade;
			flags[ForeignKeyConstraintHasNonCascadeDeleteRule] = foreignKeyConstraint.DeleteRule != Rule.Cascade;
			flags[ForeignKeyConstraintHasExtendedProperties] = foreignKeyConstraint.ExtendedProperties.Count != 0;
			flags[ForeignKeyConstraintHasSchemaName] = (string) ConstraintSchemaNameFieldInfo.GetValue(foreignKeyConstraint) != string.Empty;
			
			return flags;
		}

		private static ForeignKeyConstraint[] getForeignKeyConstraints(DataSet dataSet)
		{
			ArrayList constraints = new ArrayList();
			foreach(DataTable dataTable in dataSet.Tables)
			{
				foreach(Constraint constraint in dataTable.Constraints)
				{
					if (constraint is ForeignKeyConstraint) constraints.Add(constraint);
				}
			}
			return (ForeignKeyConstraint[]) constraints.ToArray(typeof(ForeignKeyConstraint));
		}
		
		private static bool ColumnOrdinalsMatch(DataColumn[] columns1, DataColumn[] columns2)
		{
			if (columns1 != columns2)
			{
				if ( columns1 == null || columns2 == null) return false;
				if (columns1.Length != columns2.Length) return false;

				for (int i = 0; i < columns1.Length; i++)
				{
					int matchingColumn = 0;
					while (matchingColumn < columns2.Length)
					{
						if (columns1[i].Equals(columns2[matchingColumn])) break;
						matchingColumn++;
					}
					if (matchingColumn == columns2.Length)
					{
						return false;
					}
				}
			}

			return true;
		}
		#endregion Foreign Key
		#endregion Constraints

		#region Relation
		private static readonly int RelationHasDefaultName = BitVector32.CreateMask();
		private static readonly int RelationIsNested = BitVector32.CreateMask(RelationHasDefaultName);
		private static readonly int RelationHasMultipleColumns = BitVector32.CreateMask(RelationIsNested);
		private static readonly int RelationIsPrimaryKeyOnParentTable = BitVector32.CreateMask(RelationHasMultipleColumns);
		private static readonly int RelationHasExtendedProperties = BitVector32.CreateMask(RelationIsPrimaryKeyOnParentTable);

		private static BitVector32 GetRelationFlags(DataRelation relation)
		{
			BitVector32 flags = new BitVector32();
			
			flags[RelationHasDefaultName] = relation.RelationName != string.Empty;
			flags[RelationIsNested] = relation.Nested;
			flags[RelationHasMultipleColumns] = relation.ParentColumns.Length > 1;
			flags[RelationIsPrimaryKeyOnParentTable] = relation.ParentKeyConstraint.IsPrimaryKey;
			flags[RelationHasExtendedProperties] = relation.ExtendedProperties.Count != 0;
			
			return flags;
		}
		#endregion Relation
		#endregion Flags
		
		#region Serializer
		private class FastSerializer
		{
			
			#region Fields
			private DataSet dataSet;
			private SerializationWriter writer;
			#endregion Fields
			
			#region Methods
			public byte[] Serialize(DataSet dataSet)
			{
				this.dataSet = dataSet;
				writer = new SerializationWriter();
			
				BitVector32 flags = GetDataSetFlags(dataSet);
				writer.WriteOptimized(flags);
			
				if (flags[DataSetHasName]) writer.Write(dataSet.DataSetName);
				writer.WriteOptimized(dataSet.Locale.LCID);
				if (flags[DataSetHasNamespace]) writer.Write(dataSet.Namespace);
				if (flags[DataSetHasPrefix]) writer.Write(dataSet.Prefix);
				if (flags[DataSetHasTables]) serializeTables();
				if (flags[DataSetHasForeignKeyConstraints]) serializeForeignKeyConstraints(getForeignKeyConstraints(dataSet));
				if (flags[DataSetHasRelationships]) serializeRelationships();
				if (flags[DataSetHasExtendedProperties]) serializeExtendedProperties(dataSet.ExtendedProperties);

				return getSerializedBytes();
			}
			
			public byte[] SerializeDataOnly(DataTable dataTable)
			{
				writer = new SerializationWriter();
				
				serializeRows(dataTable);								
			
				return getSerializedBytes();
			}
			
			public byte[] SerializeDataOnly(DataSet dataSet)
			{
				writer = new SerializationWriter();
				
				writer.WriteOptimized(dataSet.Tables.Count);
				foreach(DataTable dataTable in dataSet.Tables)
				{
					serializeRows(dataTable);								
				}
			
				return getSerializedBytes();
			}
			
			public byte[] Serialize(DataTable dataTable)
			{
				writer = new SerializationWriter();
			
				serializeTable(dataTable);
			
				return getSerializedBytes();
			}
			
			public byte[] SerializeSimpleDataOnly(DataTable dataTable)
			{
				writer = new SerializationWriter();
				
				int rowCount = dataTable.Rows.Count;
				writer.WriteOptimized(rowCount);
				if (rowCount != 0)
				{
					int[] calculatedColumnOrdinals = getCalculatedColumnOrdinals(dataTable);

					foreach(DataRow row in dataTable.Rows)
					{
						writer.WriteOptimized(getNonCalculatedValuesFromRowVersion(row, DataRowVersion.Current, calculatedColumnOrdinals));
					}
				}
				
				return getSerializedBytes();
			}
			#endregion Methods
			
			#region Private Methods
			private byte[] getSerializedBytes()
			{
				byte[] result = writer.ToArray();
				writer = null;
				dataSet = null;
				return result;
			}
		
			private void serializeRelationships()
			{
				DataRelationCollection relations = dataSet.Relations;
				writer.WriteOptimized(relations.Count);
			
				foreach(DataRelation relation in relations)
				{
					BitVector32 flags = GetRelationFlags(relation);
					writer.WriteOptimized(flags);
				
					if (flags[RelationHasDefaultName]) writer.Write(relation.RelationName);
				
					// Write Parent Table column info
					writer.WriteOptimized(dataSet.Tables.IndexOf(relation.ParentTable));
					if (!flags[RelationIsPrimaryKeyOnParentTable]) writeColumnOrdinals(relation.ParentColumns);
				
					// Write Child Table column info
					writeTableAndColumnOrdinals(relation.ChildColumns);
				
					if (flags[RelationHasExtendedProperties]) serializeExtendedProperties(relation.ExtendedProperties);
				}
			}

			private void serializeRows(DataTable dataTable)
			{
				int rowCount = dataTable.Rows.Count;
				writer.WriteOptimized(rowCount);
				
				if (rowCount != 0)
				{
					int[] calculatedColumnOrdinals = getCalculatedColumnOrdinals(dataTable);
				
					for(int i = 0; i < rowCount; i++)
					{
						DataRow row = dataTable.Rows[i];
				
						BitVector32 flags = GetRowFlags(row);
						writer.WriteOptimized(flags);
				
						if (!flags[RowHasOldData])
							writer.WriteOptimized(getNonCalculatedValuesFromRowVersion(row, DataRowVersion.Current, calculatedColumnOrdinals));
						else if (!flags[RowHasNewData])
							writer.WriteOptimized(getNonCalculatedValuesFromRowVersion(row, DataRowVersion.Original, calculatedColumnOrdinals));
						else
						{
							writer.WriteOptimized(
								getNonCalculatedValuesFromRowVersion(row, DataRowVersion.Current, calculatedColumnOrdinals),
								getNonCalculatedValuesFromRowVersion(row, DataRowVersion.Original, calculatedColumnOrdinals));
						}
				
						if (flags[RowHasRowError]) writer.Write(row.RowError);
						if (flags[RowHasColumnErrors])
						{
							DataColumn[] columnsInError = row.GetColumnsInError();
							writer.WriteOptimized(columnsInError.Length);
							for(int j = 0; j < columnsInError.Length; j++)
							{
								writer.WriteOptimized(columnsInError[j].Ordinal);
								writer.Write(row.GetColumnError(columnsInError[j]));
							}
						}
					}
				}
			}
			
			private int[] getCalculatedColumnOrdinals(DataTable dataTable)
			{
				ArrayList result = null;
				foreach(DataColumn column in dataTable.Columns)
				{
					if (column.Expression.Length != 0)
					{
						if (result == null) result = new ArrayList();
						result.Add(column.Ordinal);
					}
				}
				
				return (int[]) (result == null ? zeroIntegers : result.ToArray(typeof(int)));
			}
			
			private object[] getNonCalculatedValuesFromRowVersion(DataRow row, DataRowVersion version, int[] calculatedColumnOrdinals)
			{
				object[] result = GetValuesFromRowVersion(row, version);
				if (calculatedColumnOrdinals.Length != 0)
				{
					foreach(int calculatedColumnOrdinal in calculatedColumnOrdinals)
					{
						result[calculatedColumnOrdinal] = null;
					}
				}
				return result;
			}

			private void serializeTables()
			{
				DataTableCollection tables = dataSet.Tables;
				writer.WriteOptimized(tables.Count);
			
				foreach(DataTable dataTable in tables)
				{
					serializeTable(dataTable);
				}
			}

			private void serializeTable(DataTable dataTable) 
			{
				#region Flags
				BitVector32 flags = GetTableFlags(dataTable);
				writer.WriteOptimized(flags);

				if (flags[TableHasName]) writer.Write(dataTable.TableName);
				if (flags[TableHasNamespace]) writer.Write(dataTable.Namespace);
				if (flags[TableHasPrefix]) writer.Write(dataTable.Prefix);
				if (flags[TableHasSpecificCulture]) writer.WriteOptimized(dataTable.Locale.LCID);
				if (flags[TableHasTypeName])
				{
					XmlQualifiedName xmlQualifiedName = (XmlQualifiedName) TableTypeNameFieldInfo.GetValue(dataTable);
					writer.Write(xmlQualifiedName.Name);
					writer.Write(xmlQualifiedName.Namespace);
				}
				if (flags[TableHasMinimumCapacity]) writer.WriteOptimized(dataTable.MinimumCapacity);
				#endregion Flags
				
				#region Columns
				if (flags[TableHasColumns])
				{
					serializeColumns(dataTable);
					if (flags[TableHasDisplayExpression]) writer.Write(dataTable.DisplayExpression);
				}
				#endregion Columns
				
				#region Extended Properties
				if (flags[TableHasExtendedProperties]) serializeExtendedProperties(dataTable.ExtendedProperties);
				#endregion Extended Properties

				#region Data Rows
				if (flags[TableHasRows]) serializeRows(dataTable);
				#endregion Data Rows
			
				#region UniqueConstraints
				if (!flags[TableHasNoUniqueConstraints]) serializeUniqueConstraints(getUniqueConstraints(dataTable));
				#endregion UniqueConstraints
			}

			private void serializeUniqueConstraints(UniqueConstraint[] uniqueConstraints)
			{
				writer.WriteOptimized(uniqueConstraints.Length);
				foreach(UniqueConstraint uniqueConstraint in uniqueConstraints)
				{
					BitVector32 flags = GetUniqueConstraintFlags(uniqueConstraint);
					writer.WriteOptimized(flags);
				
					if (!flags[UniqueConstraintHasDefaultName])
						writer.Write(uniqueConstraint.ConstraintName);
					else
					{
						writer.WriteOptimized(int.Parse(DefaultConstraintNameMatcher.Match(uniqueConstraint.ConstraintName).Groups[1].Value));
					}
					writeColumnOrdinals(uniqueConstraint.Columns);
				
					if (flags[UniqueConstraintHasSchemaName]) writer.Write((string) ConstraintSchemaNameFieldInfo.GetValue(uniqueConstraint));
					if (flags[UniqueConstraintHasExtendedProperties]) serializeExtendedProperties(uniqueConstraint.ExtendedProperties);
				
				}
			}
			
			private void serializeForeignKeyConstraints(ForeignKeyConstraint[] foreignKeyConstraints)
			{
				writer.WriteOptimized(foreignKeyConstraints.Length);
				foreach(ForeignKeyConstraint foreignKeyConstraint in foreignKeyConstraints)
				{
					BitVector32 flags = GetForeignKeyConstraintFlags(foreignKeyConstraint);
					writer.WriteOptimized(flags);
				
					if (!flags[ForeignKeyConstraintHasDefaultName])
						writer.Write(foreignKeyConstraint.ConstraintName);
					else
					{
						writer.WriteOptimized(int.Parse(DefaultConstraintNameMatcher.Match(foreignKeyConstraint.ConstraintName).Groups[1].Value));
					}
				
					// Save Child Table column info
					writeTableAndColumnOrdinals(foreignKeyConstraint.Columns);
	
					// Save Related (parent) Table column info
					writer.WriteOptimized(dataSet.Tables.IndexOf(foreignKeyConstraint.RelatedTable));
					if (!flags[ForeignKeyConstraintIsPrimaryKeyOnParentTable]) writeColumnOrdinals(foreignKeyConstraint.RelatedColumns);

					if (flags[ForeignKeyConstraintHasAcceptRejectRule]) writer.WriteOptimized((int) foreignKeyConstraint.AcceptRejectRule);
					if (flags[ForeignKeyConstraintHasNonCascadeDeleteRule]) writer.WriteOptimized((int) foreignKeyConstraint.DeleteRule);
					if (flags[ForeignKeyConstraintHasNonCascadeUpdateRule]) writer.WriteOptimized((int) foreignKeyConstraint.UpdateRule);
				
					if (flags[ForeignKeyConstraintHasSchemaName]) writer.Write((string) ConstraintSchemaNameFieldInfo.GetValue(foreignKeyConstraint));
					if (flags[ForeignKeyConstraintHasExtendedProperties]) serializeExtendedProperties(foreignKeyConstraint.ExtendedProperties);
				
				}
			}
			
			private void serializeExtendedProperties(PropertyCollection properties)
			{
				object[] keys = new object[properties.Count];
				properties.Keys.CopyTo(keys, 0);
				writer.WriteOptimized(keys);
			
				object[] values = new object[properties.Count];
				properties.Values.CopyTo(values, 0);
				writer.WriteOptimized(values);
			
			}

			private void writeColumnOrdinals(DataColumn[] columns)
			{
				if (columns.Length == 1)
					writer.WriteOptimized(columns[0].Ordinal);
				else
				{
					int count = columns.Length;
					writer.WriteOptimized(count);
					foreach(DataColumn column in columns)
					{
						writer.WriteOptimized(column.Ordinal);
					}
				}
			}
		
			private void writeTableAndColumnOrdinals(DataColumn[] columns)
			{
				writer.WriteOptimized(dataSet.Tables.IndexOf(columns[0].Table));
				writeColumnOrdinals(columns);
			}
		
			private void serializeColumns(DataTable dataTable)
			{
				DataColumnCollection columns = dataTable.Columns;
				writer.WriteOptimized(columns.Count);
			
				foreach(DataColumn column in columns)
				{
					BitVector32 flags = GetColumnFlags(column);
					writer.WriteOptimized(flags);
				
					writer.Write(column.ColumnName);
					if (flags[ColumnIsNotStringDataType]) writer.WriteOptimized(column.DataType);
					if (flags[ColumnHasExpression]) writer.Write(column.Expression);
					if (flags[ColumnIsNotElementMappingType]) writer.WriteOptimized((int) column.ColumnMapping);
				
					if (!flags[ColumnHasAutoIncrementUnusedDefaults])
					{
						writer.WriteOptimized(Math.Abs(column.AutoIncrementSeed));
						writer.WriteOptimized(Math.Abs(column.AutoIncrementStep));
					}
					if (flags[ColumnHasCaption]) writer.Write(column.Caption);
					if (flags[ColumnHasColumnUri]) writer.Write((string) ColumnUriFieldInfo.GetValue(column));
					if (flags[ColumnHasPrefix]) writer.Write(column.Prefix);
					if (flags[ColumnHasDefaultValue]) writer.WriteObject(column.DefaultValue);
					if (flags[ColumnHasMaxLength]) writer.WriteOptimized(column.MaxLength);
					if (flags[ColumnHasExtendedProperties]) serializeExtendedProperties(column.ExtendedProperties);
				
				}

			}
			#endregion Private Methods
			
		}

		#endregion Serializer
		
		#region Deserializer
		private class FastDeserializer
		{
			#region Fields
			private DataSet dataSet;
			private SerializationReader reader;
			#endregion Fields
			
			#region Methods
			public DataTable DeserializeSimpleTypedDataTable(DataTable dataTable, byte[] serializedData)
			{
				reader = new SerializationReader(serializedData);
				
				int rowCount = reader.ReadOptimizedInt32();
				
				dataTable.BeginLoadData();
				for(int i = 0; i < rowCount; i++)
				{
					dataTable.LoadDataRow(reader.ReadOptimizedObjectArray(), true);
				}
				dataTable.EndLoadData();
				
				throwIfRemainingBytes();
				return dataTable;
			}
			
			public DataTable DeserializeDataTableDataOnly(DataTable dataTable, byte[] serializedData)
			{
				reader = new SerializationReader(serializedData);
				deserializeRows(dataTable);
				
				throwIfRemainingBytes();
				return dataTable;
			}
			
			public DataSet DeserializeDataSetDataOnly(DataSet dataSet, byte[] serializedData)
			{
				this.dataSet = dataSet;
				bool originalConstraintsSetting = dataSet.EnforceConstraints;
				dataSet.EnforceConstraints = false;
				
				reader = new SerializationReader(serializedData);
				
				int count = reader.ReadOptimizedInt32();
				for(int i = 0; i < count; i++)
				{
					deserializeRows(dataSet.Tables[i]);
				}

				dataSet.EnforceConstraints = originalConstraintsSetting;

				throwIfRemainingBytes();
				return dataSet;
			}
		
			public DataSet DeserializeDataSet(DataSet dataSet, byte[] serializedData)
			{
				this.dataSet = dataSet;
				reader = new SerializationReader(serializedData);

				dataSet.EnforceConstraints = false;
			
				BitVector32 flags = reader.ReadOptimizedBitVector32();
			
				if (flags[DataSetHasName]) dataSet.DataSetName = reader.ReadString();

				dataSet.Locale = new CultureInfo(reader.ReadOptimizedInt32());
				dataSet.CaseSensitive = flags[DataSetIsCaseSensitive];
			
				if (flags[DataSetHasNamespace]) dataSet.Namespace = reader.ReadString();
			
				if (flags[DataSetHasPrefix]) dataSet.Prefix = reader.ReadString();
			
			
				if (flags[DataSetHasTables]) deserializeTables();
				if (flags[DataSetHasForeignKeyConstraints]) deserializeForeignKeyConstraints();

				if (flags[DataSetHasRelationships]) deserializeRelationships();
				if (flags[DataSetHasExtendedProperties]) deserializeExtendedProperties(dataSet.ExtendedProperties);
				
				dataSet.EnforceConstraints = flags[DataSetAreConstraintsEnabled];

				throwIfRemainingBytes();
				return dataSet;
			}
			
			public DataTable DeserializeDataTable(DataTable dataTable, byte[] serializedData)
			{
				reader = new SerializationReader(serializedData);
				deserializeTable(dataTable);

				throwIfRemainingBytes();
				return dataTable;
			}
			#endregion Methods
			
			#region Private Methods
			private void throwIfRemainingBytes()
			{
				if (reader.BytesRemaining != 0)
				{
					throw new InvalidOperationException(string.Format("FastDeserializer did not deserialize {0:n} bytes", reader.BytesRemaining));
				}
			}
			
			private void deserializeRelationships()
			{
				int count = reader.ReadOptimizedInt32();
				for(int i = 0; i < count; i++)
				{
					BitVector32 flags = reader.ReadOptimizedBitVector32();
					DataColumn[] parentColumns;
					DataColumn[] childColumns;
				
					string relationName = flags[RelationHasDefaultName] ? reader.ReadString() : string.Empty;
					DataTable parentTable = dataSet.Tables[reader.ReadOptimizedInt32()];
				
					if (flags[RelationIsPrimaryKeyOnParentTable])
						parentColumns = parentTable.PrimaryKey;
					else
					{
						parentColumns = readColumnOrdinals(parentTable, flags[RelationHasMultipleColumns]);
					}
					
					childColumns = readTableAndColumnOrdinals(flags[RelationHasMultipleColumns]);

					DataRelation relation = new DataRelation(relationName, parentColumns, childColumns, false);
					relation.Nested = flags[RelationIsNested];
					if (flags[RelationHasExtendedProperties]) deserializeExtendedProperties(relation.ExtendedProperties);
				
					if (!dataSet.Relations.Contains(relation.RelationName)) dataSet.Relations.Add(relation);
				}
			
			}

			private void deserializeRows(DataTable dataTable)
			{
				ArrayList readOnlyColumns = null;
				int rowCount = reader.ReadOptimizedInt32();

				dataTable.BeginLoadData();
				for(int i = 0; i < rowCount; i++)
				{
					BitVector32 flags = reader.ReadOptimizedBitVector32();
					DataRow row;
				
					if (!flags[RowHasOldData])
						row = dataTable.LoadDataRow(reader.ReadOptimizedObjectArray(), !flags[RowHasNewData]);
					else if (!flags[RowHasNewData])
					{
						row = dataTable.LoadDataRow(reader.ReadOptimizedObjectArray(), true);
						row.Delete();
					} 
					else
					{
						// LoadDataRow doesn't care about ReadOnly columns but ItemArray does
						// Since only deserialization of Modified rows uses ItemArray we do this
						// only if a modified row is detected and just once
						if (readOnlyColumns == null)
						{
							readOnlyColumns = new ArrayList();
							foreach(DataColumn column in dataTable.Columns)
							{
								if (column.ReadOnly && column.Expression.Length == 0)
								{
									readOnlyColumns.Add(column);
									column.ReadOnly = false;
								}
							}
						}				
						
						object[] currentValues;
						object[] originalValues;
						reader.ReadOptimizedObjectArrayPair(out currentValues, out originalValues);
						row = dataTable.LoadDataRow(originalValues, true);
						row.ItemArray = currentValues;
					}

					if (flags[RowHasRowError]) row.RowError = reader.ReadString();
					if (flags[RowHasColumnErrors])
					{
						int columnsInErrorCount = reader.ReadOptimizedInt32();
						for(int j = 0; j < columnsInErrorCount; j++)
						{
							row.SetColumnError(reader.ReadOptimizedInt32(), reader.ReadString());
						}
					}
		
				}
				
				// Must restore ReadOnly columns if any were found when deserializing a Modified row
				if (readOnlyColumns != null && readOnlyColumns.Count != 0)
				{
					foreach(DataColumn column in readOnlyColumns)
					{
						column.ReadOnly = true;
					}
				}
				
				dataTable.EndLoadData();
				
			}
		
		
			private void deserializeUniqueConstraints(DataTable dataTable)
			{
				int count = reader.ReadOptimizedInt32();
				for(int i = 0; i < count; i++)
				{
					UniqueConstraint uniqueConstraint;
					BitVector32 flags;
					string constraintName = string.Empty;
					DataColumn[] dataColumns;

					flags = reader.ReadOptimizedBitVector32();
					if (!flags[UniqueConstraintHasDefaultName])
						constraintName = reader.ReadString();
					else
					{
						constraintName = "Constraint" + reader.ReadOptimizedInt32();
					}
					dataColumns = readColumnOrdinals(dataTable, flags[UniqueConstraintHasMultipleColumns]);
					uniqueConstraint = new UniqueConstraint(constraintName, dataColumns, flags[UniqueConstraintIsPrimaryKey]);
				
					if (flags[UniqueConstraintHasSchemaName]) ConstraintSchemaNameFieldInfo.SetValue(uniqueConstraint, reader.ReadString());
					if (flags[UniqueConstraintHasExtendedProperties]) deserializeExtendedProperties(uniqueConstraint.ExtendedProperties);

					if(!uniqueConstraint.Table.Constraints.Contains(uniqueConstraint.ConstraintName))
						uniqueConstraint.Table.Constraints.Add(uniqueConstraint);
				}
			}
		
		
			private void deserializeForeignKeyConstraints()
			{
				int count = reader.ReadOptimizedInt32();
				for(int i = 0; i < count; i++)
				{
					ForeignKeyConstraint foreignKeyConstraint;
					BitVector32 flags;
					string constraintName = string.Empty;
					DataColumn[] childColumns;
					DataColumn[] parentColumns;

					flags = reader.ReadOptimizedBitVector32();
					if (!flags[ForeignKeyConstraintHasDefaultName])
						constraintName = reader.ReadString();
					else
					{
						constraintName = "Constraint" + reader.ReadOptimizedInt32();
					}
					childColumns = readTableAndColumnOrdinals(flags[ForeignKeyConstraintHasMultipleColumns]);
				
					DataTable relatedTable = dataSet.Tables[reader.ReadOptimizedInt32()];
					if (flags[ForeignKeyConstraintIsPrimaryKeyOnParentTable])
						parentColumns = relatedTable.PrimaryKey;
					else
					{
						parentColumns = readColumnOrdinals(relatedTable, flags[ForeignKeyConstraintHasMultipleColumns]);
					}

					AcceptRejectRule acceptRejectRule = flags[ForeignKeyConstraintHasAcceptRejectRule]
					                                    	? (AcceptRejectRule) reader.ReadOptimizedInt32()
					                                    	: AcceptRejectRule.None;
					Rule deleteRule = flags[ForeignKeyConstraintHasNonCascadeDeleteRule] ? (Rule) reader.ReadOptimizedInt32() : Rule.Cascade;
					Rule updateRule = flags[ForeignKeyConstraintHasNonCascadeUpdateRule] ? (Rule) reader.ReadOptimizedInt32() : Rule.Cascade;
					
					foreignKeyConstraint = new ForeignKeyConstraint(constraintName, parentColumns, childColumns);
					foreignKeyConstraint.AcceptRejectRule = acceptRejectRule;
					foreignKeyConstraint.DeleteRule = deleteRule;
					foreignKeyConstraint.UpdateRule = updateRule;
				
					if (flags[ForeignKeyConstraintHasSchemaName]) ConstraintSchemaNameFieldInfo.SetValue(foreignKeyConstraint, reader.ReadString());
					if (flags[ForeignKeyConstraintHasExtendedProperties]) deserializeExtendedProperties(foreignKeyConstraint.ExtendedProperties);

					if(!foreignKeyConstraint.Table.Constraints.Contains(foreignKeyConstraint.ConstraintName))
						foreignKeyConstraint.Table.Constraints.Add(foreignKeyConstraint);
				}
			}
		
			private DataColumn[] readColumnOrdinals(DataTable dataTable, bool multipleColumns)
			{
				int count = multipleColumns ? reader.ReadOptimizedInt32() : 1;
				DataColumn[] columns = new DataColumn[count];
				for(int i = 0; i < count; i++)
				{
					columns[i] = dataTable.Columns[reader.ReadOptimizedInt32()];
				}
				return columns;
			}
		
			private DataColumn[] readTableAndColumnOrdinals(bool multipleColumns)
			{
				DataTable dataTable = dataSet.Tables[reader.ReadOptimizedInt32()];
				return readColumnOrdinals(dataTable, multipleColumns);
			}
		
		
			private void deserializeColumns(DataTable dataTable)
			{
				int precreatedCount = dataTable.Columns.Count;			
				int count = reader.ReadOptimizedInt32();
			
				for(int i = 0; i < count; i++)
				{
					DataColumn column = null;
					string columnName;
					Type dataType;
					string expression;
					MappingType mappingType;

					BitVector32 flags = reader.ReadOptimizedBitVector32();
				
					columnName = reader.ReadString();
					dataType = flags[ColumnIsNotStringDataType] ? reader.ReadOptimizedType() : typeof(string);
					expression = flags[ColumnHasExpression] ? reader.ReadString() : string.Empty;
					mappingType = flags[ColumnIsNotElementMappingType] ? (MappingType) reader.ReadOptimizedInt32() : MappingType.Element;
					column = new DataColumn(columnName, dataType, expression, mappingType);
				
					column.AllowDBNull = flags[ColumnIsNullable];
				
					column.AutoIncrement = flags[ColumnHasAutoIncrement];
					
					if (flags[ColumnHasAutoIncrementUnusedDefaults])
					{
						if (flags[ColumnHasAutoIncrementNegativeStep])
						{
							column.AutoIncrementSeed = -1;
							column.AutoIncrementStep = -1;
						} 
						else
						{
							column.AutoIncrementSeed = 0;
							column.AutoIncrementStep = 1;
						}
					}
					else
					{
						long seed = reader.ReadOptimizedInt64();
						long step = reader.ReadOptimizedInt64();
						if (flags[ColumnHasAutoIncrementNegativeStep])
						{
							column.AutoIncrementSeed = -seed;
							column.AutoIncrementStep = -step;
						} 
						else
						{
							column.AutoIncrementSeed = seed;
							column.AutoIncrementStep = step;
						}
					}
				
					if (flags[ColumnHasCaption]) column.Caption = reader.ReadString();
					if (flags[ColumnHasColumnUri]) ColumnUriFieldInfo.SetValue(column, reader.ReadString());
					if (flags[ColumnHasPrefix]) column.Prefix = reader.ReadString();
					if (flags[ColumnHasDefaultValue]) column.DefaultValue = reader.ReadObject();
					column.ReadOnly = flags[ColumnIsReadOnly];
					if (flags[ColumnHasMaxLength]) column.MaxLength = reader.ReadOptimizedInt32();
					if (flags[ColumnHasExtendedProperties]) deserializeExtendedProperties(column.ExtendedProperties);
				
					if (i >= precreatedCount) dataTable.Columns.Add(column);
				}

			}

			private void deserializeExtendedProperties(PropertyCollection properties)
			{
				object[] keys = reader.ReadOptimizedObjectArray();
				object[] values = reader.ReadOptimizedObjectArray();
				for(int i = 0; i < keys.Length; i++)
				{
					properties.Add(keys[i], values[i]);
				}
			}

			private void deserializeTables()
			{
				int precreatedTableCount = dataSet.Tables.Count;
				int count = reader.ReadOptimizedInt32();
			
				for(int i = 0; i < count; i++)
				{
					DataTable dataTable;
					if (i >= precreatedTableCount)
						dataTable = new DataTable();
					else
					{
						dataTable = dataSet.Tables[i];
					}
				
					deserializeTable(dataTable);

					if (!dataSet.Tables.Contains(dataTable.TableName)) dataSet.Tables.Add(dataTable);
				}
			
			}

			private void deserializeTable(DataTable dataTable) 
			{

				#region Flags
				BitVector32 flags = reader.ReadOptimizedBitVector32();
			
				dataTable.TableName = (flags[TableHasName]) ? reader.ReadString() : "";
			
				if (flags[TableHasNamespace]) dataTable.Namespace = reader.ReadString();
				if (flags[TableHasPrefix]) dataTable.Prefix = reader.ReadString();
				dataTable.CaseSensitive = flags[TableIsCaseSensitive];
				TableCaseSensitiveAmbientFieldInfo.SetValue(dataTable, flags[TableIsCaseSensitiveAmbient]);
				if (flags[TableHasSpecificCulture]) dataTable.Locale = new CultureInfo(reader.ReadOptimizedInt32());
				if (flags[TableHasTypeName])
				{
					TableTypeNameFieldInfo.SetValue(dataTable, new XmlQualifiedName(reader.ReadString(), reader.ReadString()));
				}
				TableRepeatableElementFieldInfo.SetValue(dataTable, flags[TableHasRepeatableElement]);
				if (flags[TableHasMinimumCapacity]) dataTable.MinimumCapacity = reader.ReadOptimizedInt32();
				#endregion Flags

				#region Columns
				if (flags[TableHasColumns])
				{
					deserializeColumns(dataTable);
					if (flags[TableHasDisplayExpression]) dataTable.DisplayExpression = reader.ReadString();
				}
				#endregion Columns
				
				#region Extended Properties
				if (flags[TableHasExtendedProperties]) deserializeExtendedProperties(dataTable.ExtendedProperties);
				#endregion Extended Properties
				
				#region Rows
				if (flags[TableHasRows]) deserializeRows(dataTable);
				#endregion Rows
			
				#region Unique Constraints
				if (!flags[TableHasNoUniqueConstraints])
				{
					deserializeUniqueConstraints(dataTable);
				}
				#endregion Unique Constraints
			
			}
			#endregion Private Methods
		}
		#endregion Deserializer
		#endregion Fast Serialization

	}

	/// <summary>
	/// Marker interface to signify that although the item is a Typed DataSet, it
	/// should be serialized as a plain DataSet since additional tables or columns
	/// may have been added to its schema
	/// </summary>
	public interface IModifiedTypedDataSet {}
	
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Software Developer (Senior) Hunton Information Systems Ltd.
United Kingdom United Kingdom
Simon Hewitt is a freelance IT consultant and is MD of Hunton Information Systems Ltd.

He is currently looking for contract work in London.

He is happily married to Karen (originally from Florida, US), has a lovely daughter Bailey, and they live in Kings Langley, Hertfordshire, UK.

Comments and Discussions