2015-11-19 9 views
5

は私が組み合わせる複数の式の木

パラメーター「P」はエンティティへのLINQ指定 クエリ式にバインドされていなかった次のエラーを取得しています。

私は問題を理解(​​の同じインスタンスは、ツリー内のすべての式で使用されなければならない)と私は、オンラインではなく運で見つけたソリューションを使用しようとしました。

これは、プロパティの数に基づいて、エンティティの検索のための表現を生成し、私の方法

private void SeedEntity<TEntity>(DatabaseContext context, ref TEntity entity, params Expression<Func<TEntity, object>>[] identifierExpressions) where TEntity : class 
{ 
    Expression<Func<TEntity, bool>> allExpresions = null; 

    var parameters = identifierExpressions.SelectMany(x => x.Parameters).GroupBy(x => x.Name).Select(p => p.First()).ToList(); 

    foreach (Expression<Func<TEntity, object>> identifierExpression in identifierExpressions) 
    { 
     Func<TEntity, object> vv = identifierExpression.Compile(); 
     object constant = vv(entity); 

     ConstantExpression constExp = Expression.Constant(constant, typeof(object)); 
     BinaryExpression equalExpression1 = Expression.Equal(identifierExpression.Body, constExp); 
     Expression<Func<TEntity, bool>> equalExpression2 = Expression.Lambda<Func<TEntity, bool>>(equalExpression1, parameters); 

     if (allExpresions == null) 
     { 
      allExpresions = equalExpression2; 
     } 
     else 
     { 
      BinaryExpression bin = Expression.And(allExpresions.Body, equalExpression2.Body); 
      allExpresions = Expression.Lambda<Func<TEntity, bool>>(bin, parameters); 
     } 
    } 

    TEntity existingEntity = null; 
    if (allExpresions != null) 
    { 
     existingEntity = context.Set<TEntity>().FirstOrDefault(allExpresions); 
    } 

    if (existingEntity == null) 
    { 
     context.Set<TEntity>().Add(entity); 
    } 
    else 
    { 
     entity = existingEntity; 
    } 
} 

です。

単一の式に対して正常に動作します。エラーは複数回渡すときにのみ発生します。このように呼び出され

SeedEntity(context, ref e, p=> p.Name);//Works 
SeedEntity(context, ref e, p=> p.Name, p=> p.Age);//Fails 

それは私には、以下を実行に似た何かを生成します。

することができます ConstantExpression

とe.Name & & e.Ageの交換

context.Set<TEntity>().FirstOrDefault(p=>p.Name == e.Name && p.Age == e.Age); 

上の方法を参照してください私はすべてのユニークなパラメータをつかみ、parametersにそれらを格納するpの場合は、同じ変数を使用します。これは最初ですが、params配列として渡されたExpression<Func<TEntity, bool>>のそれぞれのパラメータのインスタンスを置き換える必要があります。これが失敗します。

私は表現を列挙してのparamsに

を渡す.Update()メソッドを使用しようとした

私はまた、あなたが本当に近くだExpressionVisitor

public class ExpressionSubstitute : ExpressionVisitor 
{ 
    public readonly Expression from, to; 
    public ExpressionSubstitute(Expression from, Expression to) 
    { 
     this.from = from; 
     this.to = to; 
    } 
    public override Expression Visit(Expression node) 
    { 
     if (node == from) return to; 
     return base.Visit(node); 
    } 
} 

public static class ExpressionSubstituteExtentions 
{ 
    public static Expression<Func<TEntity, TReturnType>> RewireLambdaExpression<TEntity, TReturnType>(Expression<Func<TEntity, TReturnType>> expression, ParameterExpression newLambdaParameter) 
    { 
     var newExp = new ExpressionSubstitute(expression.Parameters.Single(), newLambdaParameter).Visit(expression); 
     return (Expression<Func<TEntity, TReturnType>>)newExp; 
    } 
} 
+0

すぐに考えてみましょう。2番目のパラメータに別の文字を使用してみましたか? (すなわち、p => p.Name、f => f.Age) –

+0

入力のおかげで、これは決してうまくいかないでしょう。それはラムダ –

+2

のために供給されるパラメータの不正確な数を投げますクエリーを組み合わせるのではなく、それらを連続して適用してみませんか? 'results =/*フルセット* /; foreach(式){結果= results.Where(式)} '? EFは 'IQueryable'を使用するので、フレームワークは必要になるまで実行を延期し、すべての述語をSQL用の単一の問合せに結合します。 – Basic

答えて

4

を使用してソリューションを試してみました。あなたのparameters変数のポイントが表示されません。それらを名前でグループ化するのは間違いです。なぜ表現からパラメータを渡すだけではないのですか?その後、必要に応じて訪問します。お客様の訪問者コードは問題ありません。

private static void SeedEntity<TEntity>(DbContext context, ref TEntity entity, params Expression<Func<TEntity, object>>[] identifierExpressions) 
     where TEntity : class 
    { 
     Expression<Func<TEntity, bool>> allExpresions = null; 

     foreach (Expression<Func<TEntity, object>> identifierExpression in identifierExpressions) 
     { 
      Func<TEntity, object> vv = identifierExpression.Compile(); 
      object constant = vv(entity); 

      ConstantExpression constExp = Expression.Constant(constant, typeof(object)); 
      BinaryExpression equalExpression1 = Expression.Equal(identifierExpression.Body, constExp); 
      Expression<Func<TEntity, bool>> equalExpression2 = Expression.Lambda<Func<TEntity, bool>>(equalExpression1, identifierExpression.Parameters); 

      if (allExpresions == null) 
      { 
       allExpresions = equalExpression2; 
      } 
      else 
      { 
       var visitor = new ExpressionSubstitute(allExpresions.Parameters[0], identifierExpression.Parameters[0]); 
       var modifiedAll = (Expression<Func<TEntity,bool>>)visitor.Visit(allExpresions); 
       BinaryExpression bin = Expression.And(modifiedAll.Body, equalExpression2.Body); 
       allExpresions = Expression.Lambda<Func<TEntity, bool>>(bin, identifierExpression.Parameters); 
      } 
     } 

     TEntity existingEntity = null; 
     if (allExpresions != null) 
     { 
      existingEntity = context.Set<TEntity>().FirstOrDefault(allExpresions); 
     } 

     if (existingEntity == null) 
     { 
      context.Set<TEntity>().Add(entity); 
     } 
     else 
     { 
      entity = existingEntity; 
     } 
    } 
+1

私は、上記の例ではなく、パラメータを置き換えることによってそのインスタンスを再利用できると思ってユニークなアクセス許可を取得しました。あなたのソリューションは動作します、ありがとうございます。今どこが間違っているのか分かります –

関連する問題