2015-11-19 16 views
5

Dostaję następujący błądŁączenie wielu wyrażeń drzewa

Parametr „p” nie był związany w określonym LINQ podmiotom wyrażeniu kwerendy.

rozumiem problemu (sama instancja ParameterExpression powinny być stosowane do wszystkich wyrażeń w drzewie) i próbowano stosować roztwory Znalazłem w Internecie, ale bez powodzenia.

To jest moja metoda

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

Generuje wyrażenie dla odnośnika podmiotu w oparciu o szereg właściwości.

Działa dobrze dla pojedynczego wyrażenia, błąd występuje tylko podczas przekazywania w wielu.

Called tak:

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

Generuje coś podobnego do mnie, wykonując następujące czynności:

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

Wymiana e.Name & & e.Age z ConstantExpression

Można zobacz w powyższej metodzie pobieram wszystkie unikalne parametry i przechowuję je w parameters na p, a następnie użyj tej samej zmiennej w całym tekście. To jest początek, ale muszę wymienić instancje parametru w każdej z Expression<Func<TEntity, bool>> przekazanej jako tablica params, w tym miejscu zawodzę.

Próbowałem wyliczyć wyrażenia i użyć przejścia .Update() metoda w params

Próbowałem również rozwiązanie używając 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

Po prostu szybka myśl, czy próbowałeś użyć innej litery dla drugiego parametru? (tj. p => p.Nazwa, f => f.Age) –

+0

Dzięki za wejście, to nigdy nie zadziałałoby, ponieważ masz tylko jeden parametr, ale twoje przejście na dwoje. Spowoduje to Niepoprawną liczbę parametrów dostarczonych dla lambda –

+2

Zamiast łączenia zapytań, dlaczego nie zastosować ich kolejno? 'results =/* Pełny zestaw * /; foreach (expression) {results = results.Where (expression)}? Ponieważ EF używa 'IQueryable', struktura odracza wykonanie, dopóki nie będzie potrzebna, a następnie połączy wszystkie predykaty w jedno zapytanie dla SQL. – Basic

Odpowiedz

4

Jesteś naprawdę blisko. Nie widzę sensu zmiennej parameters. Grupowanie ich według nazwy jest błędem. Dlaczego po prostu nie przekazać parametrów z wyrażenia? Następnie odwiedź, jeśli to konieczne. Twój kod odwiedzającego jest w porządku.

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

Złapałem unikalne uprawnienia myśląc, że mogę ponownie użyć tej instancji, zastępując parametry (nie w powyższym przykładzie). Twoje rozwiązanie działa, dziękuję bardzo. Widzę, gdzie się teraz pomyliłem –