3

次のように(制御できない)サービスからデータを取得するとします。

public class Data
{
    // an array of column names
    public string[] ColumnNames { get; set; }

    // an array of rows that contain arrays of strings as column values
    public string[][] Rows { get; get; }
}

IEnumerable<Entity>中間層では、これを、の列名がクラスのプロパティとして表されるData 場所にマップ/変換したいと思いEntityます。サービスから返されるすべてのデータではなく、一部のデータが必要な場合があるため、私はそう言いました。

変身

これは、変換を行うアルゴリズムの抽象化です。

  1. を作成して、IDictionary<string, int>個々ColumnNamesの列名を個々の行の配列インデックスに簡単にマップできるようにします。
  2. Entityリフレクションを使用してプロパティの名前を調べ、列名と照合できるようにします
  3. #1で行ったマッピングに従って、オブジェクトを繰り返しData.Rows作成し、プロパティにデータを入力します。EntityリフレクションとSetValueオンプロパティを使用してそれらを設定する可能性があります。

最適化

上位のアルゴリズムはもちろん機能しますが、リフレクションを使用するため、キャッシュを実行し、場合によってはオンザフライのコンパイルを実行する必要があります。これにより、処理速度が大幅に向上する可能性があります。

手順1と2が完了すると、文字列の配列を取得し、インデックスを使用してエンティティを直接インスタンス化し、コンパイルして将来の再利用のためにキャッシュするメソッドを実際に生成できます。

私は通常、結果のページを取得しているので、後続のリクエストは同じコンパイル済みメソッドを再利用します。

追加の事実

これは質問(および回答)に必須ではありませんが、名前が一致しない場合に列からプロパティへのマッピングに役立つ2つの属性も作成しました。私は最も明白なものMapNameAttribute(文字列を取り、オプションで大文字と小文字の区別も有効にする)を作成し、データにマップされるべきではないIgnoreMappingAttributeプロパティを作成しました。Entityただし、これらの属性は上位アルゴリズムのステップ2で読み取られるため、プロパティ名が収集され、この宣言型メタデータに従って名前が変更され、列名と一致します。

質問

そのようなメソッドを生成してコンパイルするための最良かつ最も簡単な方法は何ですか?ラムダ式?CSharpCodeProviderクラス?

同様のことを行う、生成およびコンパイルされたコードの例はありますか?マッピングはかなり一般的なシナリオだと思います。

:それまでの間、PetaPoco(およびおそらくMassive)についても検討します。これは、どちらもマッピングの目的で正確にオンザフライでコンパイルとキャッシュを実行するためです。

4

1 に答える 1

2

提案:NuGetからFastMemberを取得する

次に、以下を使用します。

var accessor = TypeAccessor.Create(typeof(Entity));

次に、ループ内で、現在の反復のmemberNameとを見つけたら、次のようにします。newValue

accessor[obj, memberName] = newValue;

これは、あなたが求めていることを実行するように設計されています。内部的には、以前に見たことがあれば、一連のタイプを維持します。TypeAccessor新しいタイプが検出されると、オンザフライ(via )の新しいサブクラスが作成されTypeBuilder、キャッシュされます。それぞれの一意TypeAccessorはそのタイプのプロパティを認識しており、基本的には次のように機能します。

switch(memberName) {
    case "Foo": obj.Foo = (int)newValue;
    case "Bar": obj.Bar = (string)newValue;
    // etc
}

これはキャッシュされているので、あなたはそれがあなたのタイプを初めて見たときにだけ(そして実際には大きなコストではなく)どんなコストも支払うだけです。それ以外の時間は無料です。ILGenerator を直接使用するため、たとえば、CodeDomを介した不要な抽象化も回避Expressionされるため、可能な限り高速になります。

dynamic(タイプ、つまり実装するタイプIDynamicMetaObjectProviderの場合、単一のインスタンスを使用してすべてのオブジェクトをサポートできることも明確にする必要があります)。


追加:

あなたができることは次のとおりです。既存のFastMemberコードを取得し、それを編集して処理MapNameAttributeし、実行IgnoreMappingAttributeWriteGetterおよび 実行しWriteSetterます。次に、すべてのブードゥーは、メンバー名ではなく、データ名で発生します。

これは、行を変更することを意味します。

il.Emit(OpCodes.Ldstr, prop.Name);

il.Emit(OpCodes.Ldstr, field.Name);

との両方WriteGetterWriteSetter、無視する必要がある場合continueはループの開始時にを実行します。foreach

于 2012-07-26T09:32:43.507 に答える