2011-02-10 15 views
2

コンパクトなフレームワークアプリケーションでprotobuf-net v1を使用して、オブジェクトをSQL Server CEデータベースに格納するためのシリアル化を処理しました。protobuf-netとsql server ce

は最近、我々は(我々は多くの種類をシリアル化していない場合は、エラーが消える。)により、非常に多くの種類を使用して明らかにバリケードを打っ参考:絶望でhttp://code.google.com/p/protobuf-net/issues/detail?id=50#c6

(私たちはすぐにリリースすることになっています)私たちはv2をダウンロードし、それを使用しています(シリアライザを事前にコンパイルせずに)。しかし、データをデシリアライズする際に、奇妙なエラーが発生することがあります。これは、未知のワイヤタイプ6と、int 32を読み込むエラーです。int型にキャストするとオーバーフローエラーが発生します。以前は同じメソッドを使用してシリアル化されていました...)私にはバイナリデータが破損しているように見えますが、SQL Serverサーバーのvarbinaryフィールドに格納して戻しています。

バイナリデータがどのように壊れる可能性がありますか?

いくつかの背景のためのMarcの答えをお読みください:

FINAL FIX(以下のコードを参照してください)。私が問題を伝えることができるのは、SetBinaryメソッドがどのように動作するかで、既存のデータを消去または切り捨てるようには見えないためです。つまり、保存されているバイナリデータが以前のデータよりも小さい場合、

私たちは、これを変更することにより、それを修正:これに

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: コードDB(コードの提案は歓迎だけでなく、問題領域)にシリアル化するために使用:

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); 
     } 
    } 
} 
+0

...さて、これは単なる問題の一部かもしれないが、我々は最初にそれを排除すべきです。 –

+0

@Marc:これは問題の大きな部分です(なぜ、protobuf-netサイトにバグを登録するのではなく、ここに投稿しています)。それは無作為で、私は再現シナリオを決定することができませんでした。それがprotobuf-netエラーか、SQL Serverのバイナリストレージを使用する際の問題かどうかはわかりません。 – Steve

+0

@Steveシノニティチェックできるようにシリアライゼーションコードを共有できますか? –

答えて

1

私は問題を見ることができます。 MemoryStreamにGetBufferを要求し、バッファの長さを使用しています。可能性のある問題は、GetBufferがオーバーサイズのバッキングバッファを返すことです。 MemoryStreamの.ToArray()を呼び出して正しいサイズのバッファを取得するか、余分な配列を割り当てる必要がない場合はGetBuffer()を呼び出すことができますが、の最初のmemStream.Lengthバイトそのバッファ。残りの部分はゴミとみなされるべきです(ほとんどすべて0になる可能性がありますが、先行ゼロはのprotobufフィールドヘッダーではではありません)。

あなたが再現可能な例を持っている場合、私は喜んで(私は著者です)を調査します

+0

すごく、私は大変感謝してそれを試みます。 v2では、事前コンパイルされたシリアライザを使用することができれば高速になります。 – Steve

+0

@Steveはい、しかしCFのためにそれをパッケージ化する*ロバスト*コードはまだ進行中です。現時点では、ランタイムのみを使用します(v1とほとんど同じですが)。迷惑なMissingMethodExceptionなどが出ないようにしてください。*完全にコンパイルされたとき(ビルドの一部として、コードが完全に磨かれているとき)それははるかに速くなければなりません。 –

+0

@Steve要約する:現時点では、v1と似たスピードであるが、端末が故障しないようにすべきである。パッケージメーカーが準備が整うとその速度が上がります。私はIKVMに切り替える必要があるかもしれないと思うが、それは大したものではない。 –

関連する問題