2015-11-19 9 views
5

Ich erhalte die folgenden FehlerKombinieren mehrerer Ausdrücke Bäume

Der Parameter ‚p‘ nicht in der angegebenen LINQ to Entities Abfrageausdruck gebunden war.

Ich verstehe das Problem (die gleiche Instanz von ParameterExpression sollte mit allen Ausdrücken im Baum verwendet werden) und haben versucht, Lösungen zu verwenden, habe ich online, aber ohne Glück gefunden.

Dies ist meine Methode

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; 
    } 
} 

Es erzeugt einen Ausdruck für die Suche eines Unternehmens auf eine Reihe von Eigenschaften basieren.

Es funktioniert gut für einen einzelnen Ausdruck, der Fehler tritt nur bei der Übergabe in mehreren.

wie folgt aufgerufen:

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

Es erzeugt etwas Ähnliches mir folgendes ausführen:

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

Ersetzen e.Name & & E.age mit einem können Sie ConstantExpression

siehe in der Methode oben greife ich alle einzigartigen params und speichern sie in parameters an der zu p, dann verwenden Sie die gleiche Variable durch. Dies ist der Start, aber dann muss ich die Instanzen des Parameters in jedem der Expression<Func<TEntity, bool>> übergeben als das params Array ersetzen, das ist, wo ich versage.

Ich habe versucht, die Ausdrücke aufzuzählen und die .Update() Methode Gang in den params

Ich habe auch versucht, eine Lösung verwenden, die 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

Nur ein kurzer Gedanke, haben Sie versucht, einen anderen Buchstaben für den zweiten Parameter zu verwenden? (dh p => p.Name, f => f.Age) –

+0

Danke für die Eingabe, dies würde nie funktionieren, da Sie nur einen Parameter haben, aber Ihre Weitergabe in zwei. Es wird Falsche Anzahl der Parameter für Lambda –

+2

liefern Anstatt die Abfragen zu kombinieren, warum sie nicht nacheinander anwenden? 'results =/* Vollständiger Satz * /; foreach (Ausdruck) {results = results.Where (Ausdruck)} '? Da EF "IQueryable" verwendet, verschiebt das Framework die Ausführung, bis es benötigt wird, und kombiniert dann alle Prädikate zu einer einzigen Abfrage für SQL. – Basic

Antwort

4

Sie verwenden sind ganz in der Nähe. Ich sehe den Punkt Ihrer parameters Variable nicht. Gruppierung nach Namen ist ein Fehler. Warum übergeben Sie nicht einfach die Parameter aus dem Ausdruck? Dann besuchen Sie wenn nötig. Dein Besuchercode ist in Ordnung.

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

Ich ergriff die eindeutigen Berechtigungen und dachte, dass ich diese Instanz durch Ersetzen der Parameter wiederverwenden könnte (nicht im obigen Beispiel). Deine Lösung funktioniert, vielen Dank. Ich sehe, wo ich jetzt falsch gelaufen bin –

Verwandte Themen