7

特定の種類のオブジェクト マッパーを作成しています。DataTable基本的には、 fields を持つaaから、 propertiesbc持つ objectに変換したいと思います(オブジェクトのクラスは手で書かれます)。多くの異なる DataTable とそれらがマップされる必要がある多くの異なるクラスがあるため、このデータのコピーを実行する汎用メカニズムを作成したいと考えています。基本的には、次の機能が必要です。abc

public T Map<T>(DataTable t) where T: new() { ... }

今、これを Reflection で行うことができますが、それは遅いです。この関数はフレームワークの中核となり、頻繁に使用されます。そこで、動的コード生成について考えています。このメソッドが特定で初めて実行されるTと、必要なリフレクションが実行され、すべての適切なマッピングを行う匿名メソッドが発行されます。次回はそのコードを実行するだけです。これは、可能な限りパフォーマンスを向上させる必要があります。

1 つだけ問題があります。実行時にコードを発行したことがありません。それ、どうやったら出来るの?を見ましたExpressionsが、一連のステートメントではなく、式しか実行できません。

次に CodeDOM とCSharpCodeProvider. そのような作業 - C# コードを文字列として生成し、オンザフライでコンパイルしてから参照を取得できます。ただし、これには C# コンパイラが関与し、まったく新しいインメモリ アセンブリが生成されます。少し聞こえる... 1 つの単純な方法としては重量級です。

もっと簡単な方法はありますか?どのアセンブリにもアタッチされていない (または既存のアセンブリにアタッチされている) 軽量の匿名メソッドを生成するものはありますか?


OK、人々が例を求めたので。

これが手書きのクラスです

class MyBusinessObject
{
    public int a;
    public string b { get; set; }
}

手動で準備された DataTable を次に示します (実際には、これは外部ライブラリから取得されます)。

DataTable t = new DataTable();
t.AddColumn("a", typeof(int));
t.AddColumn("b", typeof(string));
t.AddRow(42, "Meaning");

オンザフライで生成する必要があるメソッドは次のとおりです。

(DataRow drow, MyBusinessObject o) =>
{
    o.a = (int)drow["a"];
    o.b = (string)drow["b"];
}

簡潔にするために必要なものをいくつか省略しましたが、それが問題の核心です。

4

4 に答える 4

8

.NET 3.5 以降でコードを動的に生成する最も簡単な方法は、クラスのメソッドを使用してLINQ 式ツリーを実行可能コードに変換することです。.NET 4.0 は可能性を大幅に拡大し、.NET 3.5 の単純な表現を超えたコード構造のサポートを追加して、完全に機能するメソッドを構築できるようにしました。結果のコードは、コードの生成中に C# コンパイラが行うのと同じ種類の最適化を式ジェネレーターが適用したと仮定すると、通常のコンパイル済みコードと同じ高いパフォーマンスを提供します。CompileLambdaExpression

スニペットからコードを生成する方法は次のとおりです。

// nameToProperty is a dictionary with keys representing string parameters
// that you pass to drow's indexer, and values representing names of properties
// or fields of the target object.
private static Action<DataRow,T> MakeGetter<T>(IDictionary<string,string> nameToProperty) {
    var sequence = new List<Expression>();
    var drowParam = Expression.Parameter(typeof(DataRow));
    var oParam = Expression.Parameter(typeof(T));
    var indexer = typeof(DataRow)
        .GetDefaultMembers()
        .OfType<PropertyInfo>()
        .Where(pinf => pinf.GetIndexParameters().Length == 1
               &&      pinf.GetIndexParameters()[0].ParameterType == typeof(string))
        .Single();
    foreach (var pair in nameToProperty) {
        var indexExpr = Expression.Property(
            drowParam
        ,   indexer
        ,   Expression.Constant(pair.Key));
        sequence.Add(Expression.Assign(
            Expression.PropertyOrField(pair.Value)
        ,   indexExpr
        ));
    }
    return (Action<DataRow,T>)Expression.Lambda(
        Expression.Block(sequence)
    ,   drowParam
    ,   oParam
    ).Compile();
}

Actionこのメソッドを使用すると、必要に応じて割り当てを行うコンパイル済みの s を生成できるはずです。

于 2013-07-30T14:11:57.783 に答える
2

パーティーには遅れましたが、Marc Gravell は FastMember という優れたユーティリティを提供しています。FastMember を使用すると、DataTable からオブジェクト (動的であっても) にマップできます。

var accessor = TypeAccessor.Create(type);
string propName = // something known only at runtime

while( /* some loop of data */ ) {
   accessor[obj, propName] = rowValue;
}

私はこれを本番環境で使用しましたが、うまく機能します。

于 2013-08-22T18:23:00.717 に答える
2

私は表現をすぐに却下するつもりはありません。目標を達成するために、いくつかの異なる方法のいずれかで式を使用できます。

  1. .NET 4+ を使用している場合は、コード ブロックをサポートするために式ツリーが拡張されています。この機能をラムダ シンタックス シュガーで使用することはできませんが、このExpression.Blockメソッドを使用してコード ブロックを作成することはできます。

  2. マッピングする各フィールドのパラメーターを持つコンストラクターを使用します。生成されたコードは を模倣する可能性がありreturn new T(ExtractA(t), ExtractB(t), ...)ます。この場合、where T : new()制約を削除しMap<T>、代わりに、マッピングされた各プロパティのパラメーターを使用してリフレクションを使用して見つけることができるコンストラクターを持つオブジェクト モデル クラスに依存します。

  3. ヘルパー メソッドを使用して、一連のステートメントを単一のステートメントのように実行します。生成されたコードは、単一の特定のプロパティを設定するように設計された式から個別にコンパイルされたデリゲートであるreturn ApplyProperties(t, new T(), new[] { applyA, applyB, ... })を模倣applyAできます。メソッドは、次のようなコード内のヘルパー メソッドです。applyBAction<DataTable, T>ApplyProperties

     private T ApplyProperties<T>(DataTable t, T result, Action<DataTable, T>[] setters)
     {
         foreach (var action in setters)
         {
             action(t, result);
         }
    
         return result;
     }
    
于 2013-07-30T14:15:31.323 に答える