static internal ArgumentOutOfRangeException InvalidDataRowState(DataRowState value) { #if DEBUG switch (value) { case DataRowState.Detached: case DataRowState.Unchanged: case DataRowState.Added: case DataRowState.Deleted: case DataRowState.Modified: Debug.Fail("valid DataRowState " + value.ToString()); break; } #endif return(InvalidEnumerationValue(typeof(DataRowState), (int)value)); }
public static void SerializeDataTable(BinaryWriter bw, DataTable dt) { DataColumnCollection columns = dt.Columns; int iColCount = columns.Count; TypeCode[] colTypeCodes = new TypeCode[iColCount]; bool[] aryIsNullabl = new bool[iColCount]; // Имя таблицы bw.Write(dt.TableName); bw.Write(iColCount); // Получаем и записываем описание колонок. for (int i = 0; i < iColCount; i++) { DataColumn dc = columns[i]; // Получаем TypeCode для типа обрабатываемой колонки. TypeCode tc = Type.GetTypeCode(dc.DataType); // Запоминаем TypeCode колонки в соотвествующем массиве. colTypeCodes[i] = tc; bw.Write(dc.ColumnName); // Записываем TypeCode как Int32. Можно было бы и // сэкономить 3 байта. :) bw.Write((Int32)tc); // Создаем массив информации о поддержке колонками DBNull aryIsNullabl[i] = dc.AllowDBNull; } // Записываем битовое поле описывающее колонки поддерживающие // DBNull. Если бит поднят, значит, колонка поддерживает DBNull. BitArray bitsNull = new BitArray(aryIsNullabl); byte[] byteNull = new byte[(iColCount + 7) / 8]; bitsNull.CopyTo(byteNull, 0); bw.Write(byteNull); /////////////////////////////////////////////////////////////// // add data // count rows bw.Write(dt.Rows.Count); // Записываем строки foreach (DataRow dr in dt.Rows) { byte verFlags = 0; int iVerStart; int iVerEnd; // Разбираемся, какие версии нужно писать. // Всего есть два варианта: Original и Current DataRowState state = dr.RowState; switch (state) { // Original + Current и они равны! case DataRowState.Unchanged: iVerStart = 0; iVerEnd = 0; verFlags = 0; break; case DataRowState.Deleted: // Только Original iVerStart = 0; iVerEnd = 0; verFlags = 1; break; case DataRowState.Added: // Только Current iVerStart = 1; iVerEnd = 1; verFlags = 2; break; // Original + Current и они НЕ равны! case DataRowState.Modified: iVerStart = 0; iVerEnd = 1; verFlags = 3; break; default: throw new ApplicationException( "Недопустимое состояние строки: " + state.ToString()); } // Пишем описание версий. Временно, так как на этом мы // теряем байт на строку. Куда лучше писать дополнительные два // бита в битовое поле DbNull (хотя это и не красиво). bw.Write(verFlags); // Записываем версии текущей строки. Всего их может быть две. // в принципе можно было бы для случая DataRowState.Modified // писать только дельту данных. Но это как-нибудь потом. :) for (int iVetIndex = iVerStart; iVetIndex <= iVerEnd; iVetIndex++) { DataRowVersion drv = _aryVer[iVetIndex]; // Создаем и заполняем битовое поле. Если бит поднят, // значит, соответствующая колонка содержит DBNull. bitsNull.SetAll(false); for (int i = 0; i < iColCount; i++) { if (dr[i, drv] == DBNull.Value) { bitsNull.Set(i, true); } } bitsNull.CopyTo(byteNull, 0); // Записываем битовое поле в стрим. bw.Write(byteNull); // Перебираем колонки и пишем данные... for (int i = 0; i < iColCount; i++) { // Если колонка содержит DBNull, записывать ее значение // ненужно. object data = dr[i, drv]; if (data == DBNull.Value) // Учитываем версию! { continue; } // Записываем данные ячейки. switch (colTypeCodes[i]) { // Каждому типу соответствует переопределенная функция... case TypeCode.Boolean: bw.Write((Boolean)data); break; case TypeCode.Char: bw.Write((Char)data); break; case TypeCode.SByte: bw.Write((SByte)data); break; case TypeCode.Byte: bw.Write((Byte)data); break; case TypeCode.Int16: bw.Write((Int16)data); break; case TypeCode.UInt16: bw.Write((UInt16)data); break; case TypeCode.Int32: bw.Write((Int32)data); break; case TypeCode.UInt32: bw.Write((UInt32)data); break; case TypeCode.Int64: bw.Write((Int64)data); break; case TypeCode.UInt64: bw.Write((UInt64)data); break; case TypeCode.Single: bw.Write((Single)data); break; case TypeCode.Double: bw.Write((Double)data); break; case TypeCode.Decimal: bw.Write((Decimal)data); break; case TypeCode.DateTime: // Для DateTime приходится выпендриваться особым образом. bw.Write(((DateTime)(data)).ToFileTime()); break; case TypeCode.String: bw.Write((String)data); break; default: // На всякий случай пробуем записать неопознанный тип // виде строки. bw.Write(data.ToString()); break; } } } } }