3

現在、効率的なストレージのために SQL テーブルの行をバイナリ形式にシリアル化しています。List<object>バイナリ データを行ごとにシリアル化/逆シリアル化します。これをアップグレードして、列ごとに 1 つのフィールドで動的に生成 (発行) される POCO を使用しようとしています。

私は何時間もオンラインで検索しており、EF、T4、ExpandoObject などの ORM/フレームワークに出くわしましたが、これらはすべて動的オブジェクトを使用するか (プロパティをその場で追加/削除できます)、コンパイルする前に単に POCO を生成します。コンパイル時にテーブルのスキーマが不明であるため、テンプレートを使用できません。プロパティとその型の正確なセットを知っているため、動的オブジェクトを使用するとやり過ぎになります (そして遅くなります)。列に対応するフィールドと、それに応じて設定されたデータ型 (INT -> int、TEXT -> string) を使用して、テーブルごとに 1 つの POCO を生成する必要があります。

POCO を生成した後、 PetaPoco が静的にコンパイルされた POCO に対して行うのと同じように、出力された CIL を使用してプロパティを取得/設定します。このリグマロールのすべてが、型指定されていないリストを使用するよりも高速になり、厳密に型指定され、CLR によって高速化できる忠実度の高い POCO が得られることを願っています。これを仮定するのは正しいですか?実行時に POCO を生成することから始めてもらえますか? また、POCO を使用すると、List<object>. 基本的に、苦労する価値はありますか?発行された CIL を使用してフィールドの取得/設定を高速化する方法は既に知っています。

4

2 に答える 2

3

これは実際には非常に複雑な質問です。残念ながら、完全に答えるには、基本的にそれを書いてテストする必要がありますが、答えが得られるまで、オンザフライの POCO 生成を見ないことを強くお勧めします! 基本的に、今のところそのステップは無視してください。

パフォーマンスに関するもう 1 つの重要な問題は、どの程度の速度が必要かということです。私が絶対に最初にすることは、機能する最も簡単なことであり、それを測定します。最も簡単に動作するのは、それを にロードし、DataTableそのデータテーブルを ( を使用して) シリアル化することRemotingFormat = RemotingFormat.Binary;です。砂の中の行を提供する 10 行のコードで:

var dt = new DataTable();
dt.Load(yourDataReader);
//... any access tests
dt.RemotingFormat = SerializationFormat.Binary;
using (var file = File.Create(path))
{
    var bf = new BinaryFormatter();
    bf.Serialize(file, dt);
}
// ... also check deserialize, if that is perf-critical

通常はどちらもお勧めDataTableBinaryFormatterませんが、... この場合は大げさではないようです。

個人的には、binary- remotingDataTable -mode が実際にはひどいものではないことがわかると思います。

次のステップは、大きな努力をしなくても他に何が機能するかを確認することです。例えば:

したがって、次の行に沿って(純粋にそれがより良いかどうかを確認するために)例示的なクラスを作成したくなるでしょう。

[DataContract]
public class Foo {
    [DataMember(Order=1)] public int Id {get;set;}
    [DataMember(Order=2)] public string Name {get;set;}
    // ... more props
    // IMPORTANT: make this representative - basically, the same data
    // that you had in the data-table

    // note also include any supporting info - any indexers and interface
    // support that your core code needs
}
[DataContract]
public class FooWrapper { // just to help in the test
     [DataMember(Order=1)] public List<Foo> Items {get;set;}
}

同じテストを実行します (メイン コードはインデクサー アクセスのみを使用し.Query<Foo>(...)ますが、今のところ dapper に API を使用させます):

var data = conn.Query<Foo>(...).ToList(); // dapper
//... any access tests, just using the indexer API
using (var file = File.Create(path))
{
    var wrapper = new FooWrapper { Items = data };
    Serializer.Serialize(file, wrapper); // protobuf-net
}
// note that you deserialize via Serializer.Deserialize<FooWrapper>(file)

これのポイントは、これにより、何が達成できるかという点で期待するのが合理的であることについて、ある程度の限界が得られるということです。dapper/protobuf-net の代わりに独自のマテリアライザー/シリアライザーを自由に使用してください。ただし、これら 2 つは主にこのようなシナリオ向けに大幅に最適化されていることを謙虚に申し上げておきます。

下限と上限があれば、「それだけの価値があるか」という質問に答える賢明なデータが得られます。実行時にオブジェクトを生成することはそれほど難しくありませんが、ほとんどの人が行う必要があるよりも多くの作業が必要です。また、生成された型を可能な限り再利用するように細心の注意を払う必要があります。そのルートに進む場合、protobuf-net にはSerializer.NonGenericorを介した完全に非ジェネリックな API があることに注意してくださいRuntimeTypeModel.Default(3 つのオプションはすべて同じコアになります)。Dapperにはありませんが、喜んで追加します (インスタンスを受け入れTypeます)。とりあえず、その 1 つのステップにMakeGenericMethod/を使用することもできます。Invoke

「それだけの価値はありますか」と直接答えていないことは承知していますが、それは意図的なものです。シナリオに直接適用しないと答えられません。うまくいけば、私は代わりに、あなたのシナリオにどのように答えることができるについていくつかのヒントを提供しました. 私はあなたの調査結果を聞くことに非常に興味があります.

それだけの価値があるとわかっている場合にのみ (上記の場合、約 1 時間の作業が必要になると予想されます)、型を生成するという手間をかけます。その場合は、Sigilの使用をお勧めします。これにより、IL 生成のストレスが大幅に軽減されます。

于 2013-05-10T07:21:28.030 に答える