6

昨日、非常によく似た質問をしましたが、受け入れた回答がすべての問題を解決するとは限らないことに今日まで気づきませんでした。次のコードがあります。

public Expression<Func<TItem, object>> SelectExpression<TItem>(string fieldName)
{
    var param = Expression.Parameter(typeof(TItem), "item");
    var field = Expression.Property(param, fieldName);
    return Expression.Lambda<Func<TItem, object>>(field, 
        new ParameterExpression[] { param });
}

次のように使用されます。

string primaryKey = _map.GetPrimaryKeys(typeof(TOriginator)).Single();
var primaryKeyExpression = SelectExpression<TOriginator>(primaryKey);
var primaryKeyResults = query.Select(primaryKeyExpression).ToList();

これにより、 から主キーを引き出すことができますIQueryable<TUnknown>。問題は、このコードが 1 つの主キーでしか機能せず、複数の PK のサポートを追加する必要があることです。

SelectExpressionでは、上記のメソッドを適応させてIEnumerable<string>(主キー プロパティ名のリスト) を取得し、それらのキーを選択する式をメソッドに返させる方法はありますか?

つまり、次のようになります。

var knownRuntimePrimaryKeys = new string[] { "CustomerId", "OrderId" }`

私の選択は、(実行時に)次のことを行う必要があります。

var primaryKeys = query.Select(x=> new { x.CustomerId, x.OrderId });
4

3 に答える 3

3

新しい型を動的に作成する必要があるため、必要なことを正確に行う簡単な方法はありません (匿名型は、静的に認識されている場合にコンパイラによって作成されます)。確かに実現可能ですが、おそらく最も簡単なオプションではありません...

Tuplesを使用して同様の結果を得ることができます。

public Expression<Func<TItem, object>> SelectExpression<TItem>(string[] propertyNames)
{
    var properties = propertyNames.Select(name => typeof(TItem).GetProperty(name)).ToArray();
    var propertyTypes = properties.Select(p => p.PropertyType).ToArray();
    var tupleTypeDefinition = typeof(Tuple).Assembly.GetType("System.Tuple`" + properties.Length);
    var tupleType = tupleTypeDefinition.MakeGenericType(propertyTypes);
    var constructor = tupleType.GetConstructor(propertyTypes);
    var param = Expression.Parameter(typeof(TItem), "item");
    var body = Expression.New(constructor, properties.Select(p => Expression.Property(param, p)));
    var expr = Expression.Lambda<Func<TItem, object>>(body, param);
    return expr;
}
于 2012-01-25T10:37:31.850 に答える
2

Tuple<>匿名型はコンパイル時に認識されている必要があるため、使用できます。

public Expression<Func<TItem, object>> SelectExpression<TItem>(params string[] fieldNames)
{
    var param = Expression.Parameter(typeof(TItem), "item");
    var fields = fieldNames.Select(x => Expression.Property(param, x)).ToArray();
    var types = fields.Select(x => x.Type).ToArray();
    var type = Type.GetType("System.Tuple`" + fields.Count() + ", mscorlib", true);
    var tuple = type.MakeGenericType(types);
    var ctor = tuple.GetConstructor(types);
    return Expression.Lambda<Func<TItem, object>>(
        Expression.New(ctor, fields), 
        param
    );
}

その後:

var primaryKeyExpression = SelectExpression<TOriginator>("CustomerId", "OrderId");

次の式が生成されます。

item => new Tuple<string, string>(item.CustomerId, item.OrderId)
于 2012-01-25T10:30:37.050 に答える
1

誰かがすでに指摘しているように、基本的に実行時に匿名型を構築しようとしていますが、これは機能しません。

匿名型を使用する代わりに、必要なものを達成する方法はありますか?

これは、あなたが何を意味するかによって異なります。明らかな答えは、ディクショナリやタプルなどのランタイム構成を使用することです。しかし、おそらくあなた自身がこれを十分に認識しているので、実際のフィールド名を使用してコンパイル時に型指定された結果が必要であると想定しています。の誤用はprimaryKeysコンパイル中に発見されます。

もしそうなら、あなたの唯一のオプションは、コンパイルの前に関連するコードを生成することです。これは思ったほど悪くはありませんが、完全に透過的ではありません。スキーマを変更すると、何らかの形でコード生成を再実行する必要があります。

私たちの会社では、SubSonic に触発されて、まさにそれを実行しましが、SubSonic 自体が私たちが望んでいたものではないことがわかりました。私の意見では、それはかなりうまくいきました。

于 2012-01-25T10:31:17.593 に答える