4

フィルターとして使用できる linq.expressions.expression ステートメントを動的に生成したいと考えています。

Expression に変換したいサンプル Linq クエリを次に示します。

ctx.customer.where(c=>ctx.Invoice.Any(i=>i.customerId == c.id));

これが私の試みです

using System.Linq.Expressions;
var c = Expression.parameter(typeof(Customer),"c");
var i = Expression.parameter(typeof(Invoice),"i");

var rightPart= Expression.Equal(
 Expression.propertyorField(i,"customerId"), Expression.propertyorfield(c,"id")

手伝ってください。

4

4 に答える 4

8

手動で linq 式を作成する必要がある場合は、.Net にラムダからそのような式を作成させるだけで、その構造を調べることができます。たとえば、デバッグ下で実行します

Expression<Func<TestObject, bool>> expression = t => t.Invoice.Any(i => i.CustomerId == t.Id);

式変数を調べます。

于 2012-03-27T14:52:32.633 に答える
5

LinqExpressionから来ていると思いusing LinqExpression = System.Linq.Expressions.Expression;ます。

Anyは演算子などではないため、 特定の式タイプはありません。Anyは静的メソッドなので、そのCall式を作成する必要があります。

拡張メソッドの構文ではなく、静的メソッドの構文に基づいて式を作成する必要があります。

ctx.customer.Where(c => Enumerable.Any(ctx.Invoice, i => i.customerId == c.id));

これには多くの手順があります。すべての手順を正しく実行する時間がないため、ここでは概要を示します。c内側のラムダで使用されるパラメーターがそのラムダのパラメーターではなく、外側のラムダのパラメーターであるという事実をどのように表現するかは完全にはわかりません。概説すると、次のことが必要になります。

  1. Enumerable.Anyジェネリックメソッドの正しいオーバーロードの MethodInfo を取得します。
  2. 一般的な MethodInfo から非一般的な MethodInfo を構築します (つまり、 call MakeGenericMethod)。
  3. メソッドに最初の引数として渡す PropertyExpression を作成します ( を表しctx.Invoceます) 。
  4. メソッドに 2 番目の引数として渡すラムダ式の本体をrightPart作成します (つまり、サンプル コードで)。
  5. LambdaExpression を構築します (例: var innerLambda = Expression.Lambda(rightPart, i);)
  6. メソッドへの呼び出しを表す MethodCallExpression を構築し、MethodInfo と innerLambda を渡します。
  7. 渡す LambdaExpression を構築しますWhere(つまり、Expression.Lambda(methodCallExpression, c).

コメントに示されているようにブール式を組み合わせたい場合、4 番目のステップは異なります。

それが役立つことを願っています。

于 2012-03-27T14:52:51.163 に答える
2

内部表現を追加したことに注意してください。キャプチャされた変数があるため、作成する必要があるクロージャー コードが少しあります。

https://stackoverflow.com/a/3472250/90475のコードを使用する場合の単純化の機会に注意してください。

DebugView で式コードを取得するための最小限のコード...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace ConsoleApplication7
{
   class CTX
   {
      public List<Customer> Invoices = new List<Customer>();
   }

   class Customer
   {
      public int id;
      public static bool HasMatchingIds(Customer first, Customer second) { return true; }
   }

   class Program
   {
      static void Main(string[] args)
      {
         CTX ctx = new CTX();

         Expression<Func<Customer, bool>> expression = cust => ctx.Invoices.Any(customer => Customer.HasMatchingIds(customer, cust));
      }
   }
}

リフレクターで見るとこんな感じ。

private static void Main(string[] args)
{
    ParameterExpression CS$0$0000;
    ParameterExpression CS$0$0002;
    CTX ctx = new CTX();
    Expression<Func<Customer, bool>> expression = Expression.Lambda<Func<Customer, bool>>(
    Expression.Call(null, (MethodInfo) methodof(Enumerable.Any),
    new Expression[] { Expression.Constant(ctx.Invoices), Expression.Lambda<Func<Customer, bool>>(
    Expression.Call(null, (MethodInfo) methodof(Customer.HasMatchingIds), new Expression[] { 
    CS$0$0002 = Expression.Parameter(typeof(Customer), "customer"),
    CS$0$0000 = Expression.Parameter(typeof(Customer), "cust") }), 
    new ParameterExpression[] { CS$0$0002 }) }), new ParameterExpression[] { CS$0$0000 });
}

政府の仕事に十分近い...これは、それが些細なことではないことを示しており、元のクエリを単純化する必要があります。

また、迅速なプロトタイピングのためにLinqPadを実行してみます

于 2012-03-27T15:05:26.440 に答える
1

次のコードを追加します。

var any = Expression.Call(typeof(Queryable), "Any", new Type[] { typeof(Invoice) },
    Expression.PropertyOrField(Expression.Constant(ctx), "Invoice"),
    Expression.Lambda(rightPart, i));
var filter = Expression.Lambda<Func<Customer, bool>>(any, c);

その後、任意のメソッドfilterでパラメーターとして使用できます。IQueryable<Customer>.Where

于 2012-03-27T15:42:54.210 に答える