Подумал и решил запостить сюда, может кому-нибудь пригодится.
У меня возникла проблема передачи датасета через ремотинг.
ЗдесьАвтор: VladD2
Дата: 21.04.03
есть решение для не типизированного датасета, соответственно передается информация о структуре данных.
У меня структура известна на клиенте и сервере, и лишний трафик не нужен. Поэтому я немного поправил код:
using System;
using System.IO;
using System.Data;
using System.Collections;
namespace RSDN
{
public class DataSerializer
{
static readonly DataRowVersion[] _aryVer = new DataRowVersion[2]
{ DataRowVersion.Original, DataRowVersion.Current };
///////////////////////////////////////////////////////////////////////
// Сериализация.
public static byte[] SerializeDataSet(DataSet ds)
{
MemoryStream ms = new MemoryStream();
SerializeDataSet(ms, ds);
return ms.ToArray();
}
public static void SerializeDataSet(Stream stream, DataSet ds)
{
BinaryWriter bw = new BinaryWriter(stream);
DataTableCollection tables = ds.Tables;
//bw.Write(ds.DataSetName);
int tablesCount = 0;
foreach(DataTable dt in tables)
{ // Вообще-то foreach-и лучше на всякий пожарный избегать.
// Но мне было в лом.
if(dt.Rows.Count > 0)
tablesCount++;
}
bw.Write(tablesCount);
foreach(DataTable dt in tables)
{ // Вообще-то foreach-и лучше на всякий пожарный избегать.
// Но мне было в лом.
if(dt.Rows.Count > 0)
SerializeDataTable(bw, dt);
}
}
public static void SerializeDataTable(BinaryWriter bw, DataTable dt)
{
DataColumnCollection columns = dt.Columns;
int iColCount = columns.Count;
// Имя таблицы
bw.Write(dt.TableName);
// Записываем битовое поле описывающее колонки поддерживающие
// DBNull. Если бит поднят, значит, колонка поддерживает DBNull.
BitArray bitsNull = new BitArray(iColCount);
byte[] byteNull = new byte[(iColCount + 7) / 8];
///////////////////////////////////////////////////////////////
// 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(Type.GetTypeCode(dt.Columns[i].DataType))
{ // Каждому типу соответствует переопределенная функция...
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:
//разбираемся - не массив ли это байтов
if(dt.Columns[i].DataType == typeof(byte[]))
{
byte[] array = (byte[])data;
bw.Write(array.Length);
bw.Write(array);
}
else
{
// На всякий случай пробуем записать неопознанный тип
// виде строки.
bw.Write(data.ToString());
}
break;
}
}
}
}
}
///////////////////////////////////////////////////////////////////////
// Десериализация
private static ArrayList RemoveReadOnly(DataTable dt)
{
ArrayList roColumnsNumbers = null;
int numCols = dt.Columns.Count;
for (int i = 0; i < numCols; i++)
{
if(dt.Columns[i].ReadOnly)
{
if(roColumnsNumbers == null)
roColumnsNumbers = new ArrayList();
roColumnsNumbers.Add(i);
dt.Columns[i].ReadOnly = false;
}
}
return roColumnsNumbers;
}
private static void ResetReadOnly(ArrayList roColumnsNumbers, DataTable dt)
{
if(roColumnsNumbers != null)
{
int numCols = roColumnsNumbers.Count;
for (int i = 0; i < numCols; i++)
{
dt.Columns[(int)roColumnsNumbers[i]].ReadOnly = true;
}
}
}
public static void DeserializeTable(BinaryReader br, DataTable dt)
{
ArrayList roColumnsNumbers = RemoveReadOnly(dt);
try
{
DataRow dr;
dt.BeginLoadData();
int iColCount = dt.Columns.Count;
byte[] byteNull = new byte[(iColCount + 7) / 8];
int counRows = br.ReadInt32();
DataRowCollection rows = dt.Rows;
for(int r = 0; r < counRows; r++)
{
// Читаем описание версий. Временно, так как на этом мы
// теряем байт на строку.
byte verFlags = br.ReadByte();
int iVerStart;
int iVerEnd;
DataRowState drs;
switch(verFlags)
{
// Original + Current и они равны!
case 0: // DataRowState.Unchanged
iVerStart = 0;
iVerEnd = 0;
drs = DataRowState.Unchanged;
break;
case 1: // DataRowState.Deleted Только Original
iVerStart = 0;
iVerEnd = 0;
drs = DataRowState.Deleted;
break;
case 2: // DataRowState.Added Только Current
iVerStart = 1;
iVerEnd = 1;
drs = DataRowState.Added;
break;
// Original + Current и они НЕ равны!
case 3: // DataRowState.Modified
iVerStart = 0;
iVerEnd = 1;
drs = DataRowState.Modified;
break;
default:
throw new ApplicationException(
"Недопустимое состояние строки. Сбой при загрузке.");
}
// Считываем данные.
dr = dt.NewRow();
rows.Add(dr);
dr.BeginEdit();
// Считываем версии текущей строки.
for(int iVetIndex = iVerStart; iVetIndex <= iVerEnd; iVetIndex++)
{
br.Read(byteNull, 0, byteNull.Length);
BitArray bitsNull = new BitArray(byteNull);
for(int i = 0; i < dt.Columns.Count; i++)
{
if(bitsNull.Get(i))
{
dr[i] = DBNull.Value;
continue;
}
switch(Type.GetTypeCode(dt.Columns[i].DataType))
{
case TypeCode.Boolean: dr[i] = br.ReadBoolean(); break;
case TypeCode.Char: dr[i] = br.ReadChar(); break;
case TypeCode.SByte: dr[i] = br.ReadSByte(); break;
case TypeCode.Byte: dr[i] = br.ReadByte(); break;
case TypeCode.Int16: dr[i] = br.ReadInt16(); break;
case TypeCode.UInt16: dr[i] = br.ReadUInt16(); break;
case TypeCode.Int32: dr[i] = br.ReadInt32(); break;
case TypeCode.UInt32: dr[i] = br.ReadUInt32(); break;
case TypeCode.Int64: dr[i] = br.ReadInt64(); break;
case TypeCode.UInt64: dr[i] = br.ReadUInt64(); break;
case TypeCode.Single: dr[i] = br.ReadSingle(); break;
case TypeCode.Double: dr[i] = br.ReadDouble(); break;
case TypeCode.Decimal: dr[i] = br.ReadDecimal(); break;
case TypeCode.DateTime:
dr[i] = DateTime.FromFileTime(br.ReadInt64());
break;
case TypeCode.String: dr[i] = br.ReadString(); break;
default:
if(dt.Columns[i].DataType == typeof(byte[]))
{
int lenght = br.ReadInt32();
byte[] array = new byte[lenght];
br.Read(array, 0, lenght);
dr[i] = array;
}
else
{
dr[i] = Convert.ChangeType(br.ReadString(),
Type.GetTypeCode(dt.Columns[i].DataType));
}
break;
}
}
if(iVetIndex == 0)
{
dr.AcceptChanges();
if(iVerEnd > 0)
dr.BeginEdit();
}
}
if(drs == DataRowState.Deleted)
dr.Delete();
dr.EndEdit();
}
dt.EndLoadData();
}
catch
{
dt.RejectChanges();
throw;
}
finally
{
ResetReadOnly(roColumnsNumbers, dt);
}
}
public static void DeserializeDataSet(Stream stream, DataSet ds)
{
BinaryReader br = new BinaryReader(stream);
int counTables = br.ReadInt32();
for(int t = 0; t < counTables; t++)
{
DeserializeTable(br, ds.Tables[br.ReadString()]);
}
}
public static void DeserializeDataSet(byte[] data, DataSet ds)
{
MemoryStream ms = new MemoryStream(data);
DeserializeDataSet(ms, ds);
}
}
}
Буду благодарен за любые замечания