Protobuf-Net и SQL Server CE - PullRequest
       11

Protobuf-Net и SQL Server CE

2 голосов
/ 10 февраля 2011

Мы успешно используем protobuf-net v1 в компактном фреймворковом приложении для сериализации наших объектов для хранения в базе данных sql server ce.

Недавно мы столкнулись с препятствиями, по-видимому, из-за использования слишком большого количества типов (если мы не сериализовали столько типов, ошибка исчезнет). Ссылка: http://code.google.com/p/protobuf-net/issues/detail?id=50#c6

В отчаянии (мы должны скоро выпустить) мы загрузили v2 и использовали его (без предварительной компиляции сериализаторов). Тем не менее, мы иногда получаем странные ошибки при десериализации данных - неизвестный проводной тип 6 и ошибка чтения int-32 - каким-то образом он получает ошибку переполнения при преобразовании в int, что не имеет смысла из-за факта что он был предварительно сериализован с использованием того же метода ...) Мне кажется, что мы получаем некоторое повреждение двоичных данных - но мы просто храним в поле varbinary в sql server ce и извлекаем его обратно.

У кого-нибудь есть идеи, как двоичные данные могут быть повреждены? (См. Код ниже)

ФИНАЛЬНЫЙ ИСПРАВЛЕНИЕ:

Пожалуйста, прочитайте ответ Марка для некоторой предыстории. Самое лучшее, что я могу сказать, это то, как работает метод SetBinary - он, кажется, не очищает или не усекает существующие данные - поэтому, если сохраняемые двоичные данные меньше, чем предыдущий мусорный массив, оставленный в конце. 1012 *

Мы исправили это, изменив это:

if (buffer.Length > 0)
{
    record.SetBytes(insertSet.GetOrdinal(SerializedDataColumnName), 0, buffer, 0, buffer.Length);
}

к этому:

if (buffer.Length > 0)
{
    record.SetValue(insertSet.GetOrdinal(SerializedDataColumnName), null);
    record.SetBytes(insertSet.GetOrdinal(SerializedDataColumnName), 0, buffer, 0, buffer.Length);
}

Спасибо.

UPDATE: Код, используемый для сериализации в БД (приветствуются предложения кода и проблемные области):

command.CommandType = CommandType.TableDirect;
MemoryStream ms = null;
using (SqlCeResultSet insertSet = command.ExecuteResultSet(ResultSetOptions.Updatable))
{
    foreach (var item in items)
    {
        ms = new MemoryStream();
        Serializer.Serialize<T>(ms, item);
        var record = insertSet.CreateRecord();
        var buffer = ms.GetBuffer();
        if (buffer.Length > 0)
        {
            record.SetBytes(insertSet.GetOrdinal(SerializedDataColumnName), 0, buffer, 0, buffer.Length);
        }
        else
        {
            record.SetValue(insertSet.GetOrdinal(SerializedDataColumnName), null);
        }
        insertSet.Update();
    }
}
if (ms != null)
{
    ms.Dispose();
}

Код, используемый для десериализации:

using (var ms = new MemoryStream())
{
    using (SqlCeResultSet recordSet = command.ExecuteResultSet(ResultSetOptions.Scrollable))
    {
        //var serializer = null; //ServiceDepository.TryGetProvider<TypeModel, T>();
        while (recordSet.Read())
        {
            if (!recordSet.IsDBNull(recordSet.GetOrdinal(SerializedDataColumnName)))
            {
                var count = recordSet.GetBytes(recordSet.GetOrdinal(SerializedDataColumnName), 0, null, 0, 1);
                var bytes = new byte[count];
                recordSet.GetBytes(recordSet.GetOrdinal(SerializedDataColumnName), 0, bytes, 0, (int)count);
                if (bytes.Length > 0)
                {
                    var ms2 = new MemoryStream(bytes);
                    item = Serializer.Deserialize<T>(ms2);
                }
            }
            if (item == null)
            {
                //handle 'empty' items -- there were no properties
                //  that needed to be serialized
                item = new T();
            }
            list.Add(item);
        }
    }
}

1 Ответ

1 голос
/ 10 февраля 2011

Я вижу проблему; вы запрашиваете MemoryStream для GetBuffer и используете длину буфера. Вероятная проблема здесь заключается в том, что GetBuffer возвращает слишком большой резервный буфер; Вы должны либо вызвать .ToArray () в MemoryStream для получения буфера правильного размера, либо, если вы не хотите выделять дополнительный массив, вы можете вызвать GetBuffer (), но вы должны сохранить только первый memStream . Длинные байты из этого буфера; остальное следует считать мусором (это, скорее всего, все нули, но начальный ноль недопустим в заголовке поля protobuf).

Теперь, это может быть просто частью проблемы, но мы должны сначала устранить ее ...

...