1

理解できないラムダ式ツリーの問題があります。動的な linq Select ステートメントを作成しようとしています。

ここに作成された動的リポジトリがあります:

private static dynamic GetRepository(Type type)
{
    dynamic repository = typeof(IFactory).GetMethod("Create").MakeGenericMethod(typeof(IRepository<>).MakeGenericType(type)).Invoke(ObjectFactory.Instance, new object[] { });
    return repository;
}

これで、コンパイル時に x と SomeProperty がわからないだけで、これを呼び出す必要があります。私は SomeProperty 名を持つ PropertyInfo propertyInfo と x タイプの Type objectType を持っています。次の例外を除いて、目標 1 で失敗します。

GetMethod(文字列名)でのSystem.Reflection.AmbiguousMatchException

コード:

private SomeObject CreateSomeObject (PropertyInfo propertyInfo, Type objectType)
{
    var param = Expression.Parameter(objectType, "x");
    MemberExpression expression = Expression.PropertyOrField(param, propertyInfo.Name);

    //Goal 1: var selectExpression = Expression.Lambda<Func<objectType, object>>(expression, param);
    var selectExpression = typeof(Expression).GetMethod("Lambda").MakeGenericMethod(typeof(Func<,>)
    .MakeGenericType(objectType, typeof(object)))
    .Invoke((object)null, new object[] { expression, param });

    // Goal 2: List<object> list = GetRepository(objectType).FindAllQuery().Select(x => x.SomeProperty).ToList();
    List<object> list = GetRepository(objectType).FindAll().Select(selectExpression);
}

これを解決するには?

更新 1:

Lambda メソッドの選択方法、「param」パラメータのパック方法を変更し、「expression」にオブジェクト コンバーターを追加しました。

private SomeObject CreateSomeObject (PropertyInfo propertyInfo, Type objectType)
{
    var param = Expression.Parameter(objectType, "x");
    Expression expression = Expression.Convert(Expression.PropertyOrField(param, propertyInfo.Name), typeof(object));

    //Goal 1: var selectExpression = Expression.Lambda<Func<objectType, object>>(expression, param);
    var selectExpression = typeof(Expression).GetMethods().First(m => m.Name == "Lambda" && m.IsGenericMethod)
    .MakeGenericMethod(typeof(Func<,>)
    .MakeGenericType(objectType, typeof(object)))
    .Invoke((object)null, new object[] { expression, new [] { param }});

    // Goal 2: List<object> list = GetRepository(objectType).FindAllQuery().Select(x => x.SomeProperty).ToList();
    List<object> list = GetRepository(objectType).FindAll().Select(selectExpression);
}

ただし、目標 2 (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException) でこの例外が発生することを知っています。

「System.Collections.Generic.List」には「Select」の定義が含まれていません

System.Linq で定義されており、拡張メソッドであるため、これは部分的に正しいです。これを機能させるにはどうすればよいですか?

4

1 に答える 1

3

例外をスローするコードは

typeof(Expression).GetMethod("Lambda")

Lambda型で定義された18 個のメソッドが呼び出されるためExpression(したがってAmbiguousMatchException)。

GetMethod(string methodName)オーバーロードがない場合に適しています。この場合GetMethods()、必要なものを使用してから除外します。

あなたの場合、正しいオーバーロードは

Expression.Lambda<TDelegate>(Expression body, params ParameterExpression[] parameters)

パラメーターの数とその型をチェックすることで、適切なオーバーロードを検証する関数を作成することもできますが、私はより簡単な代替方法を見つけました。.ToString()表現によってメソッドをフィルター処理することです。この場合は次のようになります。

System.Linq.Expressions.Expression`1[TDelegate] Lambda[TDelegate](System.Linq.Expressions.Expression, System.Linq.Expressions.ParameterExpression[])

パラメータを渡す方法にも問題があります ( new object[] { expression, param })。2 番目のパラメーターは型ParameterExpressionではなく(配列) であるため、単に の代わりにParameterExpression[]渡す必要があります。通常のコードで呼び出すと、 として定義されているため、そのように機能します。new[]{param}paramparams ParameterExpression[]

結論として、次のコードはあなたのケースで機能するはずです:

const string methodSignature = 
    "System.Linq.Expressions.Expression`1[TDelegate] Lambda[TDelegate]" +
    "(System.Linq.Expressions.Expression, System.Linq.Expressions.ParameterExpression[])";

var lambdaMethod = typeof (Expression).GetMethods()
    .Single(mi => mi.ToString() == methodSignature);

var funcType = typeof (Func<,>).MakeGenericType(objectType, typeof (object));

var genericLambda = lambdaMethod.MakeGenericMethod(funcType);

var selectExpression = genericLambda.Invoke(null, new object[] { expression, new[] { param } });
于 2012-09-20T10:18:55.237 に答える