1

protobuf-netがDataContractsの代わりになるかどうかを確認しています。優れたパフォーマンスに加えて、それは本当に素晴らしいライブラリです。私が抱えている唯一の問題は、.NETシリアライザーが現在デ/シリアライズしているものを想定していないことです。特に、型指定されたオブジェクトへの参照を含むオブジェクトは問題です。

[DataMember(Order = 3)]
public object Tag1 // The DataContract did contain a object which becomes now a SimulatedObject
{
    get;
    set;
}

私は、異なる強く型付けされたフィールドに可能なタイプごとに格納する小さな汎用ヘルパーを使用して、プロトコルバッファを使用してオブジェクトを模倣しようとしました。

これは、関連のないさまざまなタイプに逆シリアル化/シリアル化するフィールドを処理するための推奨されるアプローチですか?

以下は、最大10個の異なるタイプを保持できるSimulatedObjectのサンプルコードです。

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using ProtoBuf;
using System.Diagnostics;

[DataContract]
public class SimulatedObject<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10>
{
    [DataMember(Order = 20)]
    byte FieldHasValue; // the number indicates which field actually has a value

    [DataMember(Order = 1)]
    T1 I1;

    [DataMember(Order = 2)]
    T2 I2;

    [DataMember(Order = 3)]
    T3 I3;

    [DataMember(Order = 4)]
    T4 I4;

    [DataMember(Order = 5)]
    T5 I5;

    [DataMember(Order = 6)]
    T6 I6;

    [DataMember(Order = 7)]
    T7 I7;

    [DataMember(Order = 8)]
    T8 I8;

    [DataMember(Order = 9)]
    T9 I9;

    [DataMember(Order = 10)]
    T10 I10;

    public object Data
    {
        get
        {
            switch(FieldHasValue)
            {
                case 0: return null;
                case 1: return I1;
                case 2: return I2;
                case 3: return I3;
                case 4: return I4;
                case 5: return I5;
                case 6: return I6;
                case 7: return I7;
                case 8: return I8;
                case 9: return I9;
                case 10: return I10;
                default:
                    throw new NotSupportedException(String.Format("The FieldHasValue field has an invlaid value {0}. This indicates corrupt data or incompatible data layout chagnes", FieldHasValue));
            }
        }
        set
        {
            I1 = default(T1);
            I2 = default(T2);
            I3 = default(T3);
            I4 = default(T4);
            I5 = default(T5);
            I6 = default(T6);
            I7 = default(T7);
            I8 = default(T8);
            I9 = default(T9);
            I10 = default(T10);


            if (value != null)
            {
                Type t = value.GetType();
                if (t == typeof(T1))
                {
                    FieldHasValue = 1;
                    I1 = (T1) value;
                }
                else if (t == typeof(T2))
                {
                    FieldHasValue = 2;
                    I2 = (T2) value;
                }
                else if (t == typeof(T3))
                {
                    FieldHasValue = 3;
                    I3 = (T3) value;
                }
                else if (t == typeof(T4))
                {
                    FieldHasValue = 4;
                    I4 = (T4) value;
                }
                else if (t == typeof(T5))
                {
                    FieldHasValue = 5;
                    I5 = (T5) value;
                }
                else if (t == typeof(T6))
                {
                    FieldHasValue = 6;
                    I6 = (T6) value;
                }
                else if (t == typeof(T7))
                {
                    FieldHasValue = 7;
                    I7 = (T7) value;
                }
                else if (t == typeof(T8))
                {
                    FieldHasValue = 8;
                    I8 = (T8) value;
                }
                else if (t == typeof(T9))
                {
                    FieldHasValue = 9;
                    I9 = (T9) value;
                }
                else if (t == typeof(T10))
                {
                    FieldHasValue = 10;
                    I10 = (T10) value;
                }
                else
                {
                    throw new NotSupportedException(String.Format("The type {0} is not supported for serialization. Please add the type to the SimulatedObject generic argument list.", t.FullName));
                }
            }
        }
    }
}

[DataContract]
class Customer
{
    /* 
    [DataMember(Order = 3)]
    public object Tag1 // The DataContract did contain a object which becomes now a SimulatedObject
    {
        get;
        set;
    }
    */

    [DataMember(Order = 3)]
    public SimulatedObject<bool, Other, Other, Other, Other, Other, Other, Other, Other, SomethingDifferent> Tag1 // Can contain up to 10 different types
    {
        get;
        set;
    }



    [DataMember(Order = 4)]
    public List<string> Strings
    {
        get;
        set;
    }
}

[DataContract]
public class Other
{
    [DataMember(Order = 1)]
    public string OtherData
    {
        get;
        set;
    }
}

[DataContract]
public class SomethingDifferent
{
    [DataMember(Order = 1)]
    public string OtherData
    {
        get;
        set;
    }

}


class Program
{
    static void Main(string[] args)
    {
        Customer c = new Customer
        {
            Strings = new List<string> { "First", "Second", "Third" },
            Tag1 = new SimulatedObject<bool, Other, Other, Other, Other, Other, Other, Other, Other, SomethingDifferent>
                    {
                        Data = new Other {  OtherData = "String value "}
                    }
        };

        const int Runs = 1000 * 1000;
        var stream = new MemoryStream();

        var sw = Stopwatch.StartNew();

        Serializer.Serialize<Customer>(stream, c);
        sw = Stopwatch.StartNew();
        for (int i = 0; i < Runs; i++)
        {
            stream.Position = 0;
            stream.SetLength(0);
            Serializer.Serialize<Customer>(stream, c);
        }
        sw.Stop();
        Console.WriteLine("Data Size with Protocol buffer Serializer: {0}, {1} objects did take {2}s", stream.ToArray().Length, Runs, sw.Elapsed.TotalSeconds);

        stream.Position = 0;
        var newCustw = Serializer.Deserialize<Customer>(stream);

        sw = Stopwatch.StartNew();
        for (int i = 0; i < Runs; i++)
        {
            stream.Position = 0;
            var newCust = Serializer.Deserialize<Customer>(stream);
        }
        sw.Stop();
        Console.WriteLine("Read object with Protocol buffer deserializer: {0} objects did take {1}s", Runs, sw.Elapsed.TotalSeconds);

    }
}
4

3 に答える 3

1

いいえ、このソリューションを長期的に維持することは困難です。

シリアル化プロセスでシリアル化されたデータの前にシリアル化されたタイプのフルネームを追加し、逆シリアル化プロセスの最初にタイプ名を読み取ることをお勧めします(protobufソースコードを変更する必要はありません)

補足として、逆シリアル化プロセスでオブジェクトタイプを混在させないようにする必要があります。既存の.netアプリケーションを更新していて、再設計できないと想定しています。

更新:サンプルコード

public byte[] Serialize(object myObject)
{
    using (var ms = new MemoryStream())
    {
        Type type = myObject.GetType();
        var id = System.Text.ASCIIEncoding.ASCII.GetBytes(type.FullName + '|');
        ms.Write(id, 0, id.Length);
        Serializer.Serialize(ms, myObject);
        var bytes = ms.ToArray();
        return bytes;
    }
}

public object Deserialize(byte[] serializedData)
{
    StringBuilder sb = new StringBuilder();
    using (var ms = new MemoryStream(serializedData))
    {
        while (true)
        {
            var currentChar = (char)ms.ReadByte();
            if (currentChar == '|')
            {
                break;
            }

            sb.Append(currentChar);
        }

        string typeName = sb.ToString();

        // assuming that the calling assembly contains the desired type.
        // You can include aditional assembly information if necessary
        Type deserializationType = Assembly.GetCallingAssembly().GetType(typeName);

        MethodInfo mi = typeof(Serializer).GetMethod("Deserialize");
        MethodInfo genericMethod = mi.MakeGenericMethod(new[] { deserializationType });
        return genericMethod.Invoke(null, new[] { ms });
    }
}
于 2012-11-05T16:23:36.260 に答える
0

私は現在同様の作業を行っており、ライブラリの最初のバージョンをすでに提供しています:http: //bitcare.codeplex.com/

現在のバージョンはまだジェネリックをサポートしていませんが、近いうちに追加する予定です。私はそこにのみソースコードをアップロードしました-ジェネリックスの準備ができたら、binバージョンも準備します...

両方の側(クライアントとサーバー)が何をシリアル化/逆シリアル化するかを知っていることを前提としているため、完全なメタデータ情報をそこに埋め込む理由はありません。このため、シリアル化の結果は非常に小さく、生成されたシリアライザーは非常に高速に動作します。データディクショナリがあり、スマートデータストレージを使用し(重要なビットのみを簡潔に格納します)、必要に応じて最終的な圧縮を行います。必要な場合は、問題が解決するかどうか試してみてください。

ライセンスはGPLですが、すぐに制限の少ないものに変更します(商用利用も無料ですが、GPLのように自己責任で)

于 2012-11-12T14:44:25.843 に答える
-1

codeplexにアップロードしたバージョンは、私の製品の一部で動作しています。もちろん、さまざまな単体テストのセットでテストされています。vs2012と.net4.5に移植し、次のリリース用に新しいテストケースのセットを作成することにしたため、ここにはアップロードされません。

私は抽象的な(いわゆるオープン)ジェネリックを扱っていません。パラメータ化されたジェネリックを処理します。データコントラクトの観点からは、パラメーター化されたジェネリックは単なる特殊なクラスであるため、通常どおりに処理できます(他のクラスと同様)。違いは、オブジェクトの構築のみとストレージの最適化です。

Nullable <>にnull値に関する情報を格納する場合、ストレージストリームに1ビットしかかかりません。null値でない場合は、ジェネリックパラメーターとして提供されるタイプに従ってシリアル化を行います(たとえば、1ビットから取得できるDateTimeのシリアル化を行いますいわゆるデフォルト値を数バイトに設定します)。目標は、クラスのデータコントラクトに関する現在の知識に従ってシリアル化コードを生成することでした。その場で実行してメモリと処理能力を浪費するのではありません。コード生成中にジェネリックに基づくクラスのプロパティを見ると、そのジェネリックのすべてのプロパティとすべてのプロパティのタイプがわかります:)この観点からは、具体的なクラスです。

すぐにライセンスを変更します。提供されているライセンスタイプのリストから選択できると思いますが、独自のライセンステキストを提供できないため、最初にその方法を理解する必要があります:)。Newtonsoft.Jsonのライセンスもありますが、変更方法がわかりません...

ドキュメントはまだ提供されていませんが、要するに、独自のシリアル化テストを準備するのは簡単です。効果的な方法で保存/シリアル化するタイプでアセンブリをコンパイルしてから、シリアル化ライブラリに* .ttファイルを作成し(personクラスのように-依存関係をチェックし、他の依存クラスのコードも生成します)、ファイルを保存する必要があります(それらを保存すると、シリアル化ライブラリと連携するためのすべてのコードが生成されます)。ビルド構成でタスクを作成して、ソリューションをビルドするたびにttファイルからソースコードを再生成することもできます(おそらく、Entity Frameworkはビルド中に同様の方法でコードを生成します)。今すぐシリアル化ライブラリをコンパイルして、結果のパフォーマンスとサイズを測定できます。

Azure TableとBlobストレージでエンティティを効果的に使用するために、フレームワークにこのシリアル化ライブラリが必要なので、すぐに初期リリースを終了する予定です...

于 2012-11-13T14:50:41.103 に答える