Domanda

io sto usando i miei metodi di estensione di IQueryable <> per creare query chainable quali FindAll (). FindInZip (12345) .NameStartsWith ( "XYZ"). OrderByHowIWantIt (), ecc, che poi su esecuzione differita crea una singola query in base alla mia catena di metodi di estensione.

Il problema di questo, però, è che tutto Dov'è nella catena di estensione (FindXYZ, FindInZip etc.) saranno sempre coniugare, come e il che significa che non posso fare qualcosa di simile:

FindAll (). FirstNameStartsWith ( "X"). OrLastNameStartsWith ( "Z"), perché non so come posso iniettare il o in un separato dove metodo.

Qualsiasi idea di come posso risolvere questo?


supplementare; Finora ho capito come catena di espressioni come O se mi avvolgo loro (per esempio CompileAsOr (FirstNameStartsWith ( "A"). LastNameStartsWith ( "Z"). OrderBy (..))

Quello che sto cercando di fare anche se è leggermente più complicato (e PredicateBuilder non aiuta qui ..) in quanto voglio un IQueryable in seguito per accedere fondamentalmente la dove le condizioni che sono state stabilite prima senza dover avvolgere loro per creare l'O tra di loro.

Come ogni metodo di estensione restituisce IQueryable <> Mi sembra di capire che dovrebbe avere la conoscenza sullo stato attuale delle condizioni di query da qualche parte, che mi porta a credere che ci dovrebbe essere un modo automatizzato o la creazione di una o in tutte le condizioni preliminari Dove senza dover avvolgere ciò che si desidera in OR.

È stato utile?

Soluzione

Sto assumendo le diverse parti della query sono conosciute solo in fase di esecuzione, vale a dire non si può semplicemente utilizzare || in un where ...

Una possibilità è pigro Concat - ma questo tende a portare a scarsa TSQL ecc; tuttavia, tendo a essere propensi a scrivere Expressions personalizzati invece. L'approccio di prendere dipende da ciò che il provider è, come LINQ to SQL supporta diverse opzioni di EF (per esempio) - che ha un impatto vero e proprio qui (dal momento che non è possibile utilizzare sottoespressioni con EF). Ci può dire quali?


Ecco un po 'di codice che dovrebbe funzionare con LINQ to SQL; se si costruisce una matrice (o una lista, e chiamare .ToArray()) di espressioni, dovrebbe funzionare bene; esempio è LINQ to Objects, ma dovrebbe funzionare:

    static void Main()
    {
        var data = (new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).AsQueryable();

        var predicates = new List<Expression<Func<int, bool>>>();
        predicates.Add(i => i % 3 == 0);
        predicates.Add(i => i >= 8);           

        foreach (var item in data.WhereAny(predicates.ToArray()))
        {
            Console.WriteLine(item);
        }
    }

    public static IQueryable<T> WhereAny<T>(
        this IQueryable<T> source,
        params Expression<Func<T,bool>>[] predicates)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (predicates == null) throw new ArgumentNullException("predicates");
        if (predicates.Length == 0) return source.Where(x => false); // no matches!
        if (predicates.Length == 1) return source.Where(predicates[0]); // simple

        var param = Expression.Parameter(typeof(T), "x");
        Expression body = Expression.Invoke(predicates[0], param);
        for (int i = 1; i < predicates.Length; i++)
        {
            body = Expression.OrElse(body, Expression.Invoke(predicates[i], param));
        }
        var lambda = Expression.Lambda<Func<T, bool>>(body, param);
        return source.Where(lambda);
    }

Altri suggerimenti

PredicateBuilder<T> . E 'probabilmente quello che volete.

    List<string> fruits =
        new List<string> { "apple", "passionfruit", "banana", "mango",
               "orange", "blueberry", "grape", "strawberry" };

    var query = fruits.AsQueryable();

    // Get all strings whose length is less than 6.
    query = query.Where(fruit => fruit.Length < 6);

    // Hope to get others where length is more than 8.  But you can't, they're gone.
    query = query.Where(fruit => 1 == 1 || fruit.Length > 8);

    foreach (string fruit in query)
        Console.WriteLine(fruit);

In un mondo ideale Personalmente ritengo operatori || e && sarebbe la più semplice e leggibile. Tuttavia non si compila.

  

operatore '||' non può essere applicato a operandi di tipo 'Expression<Func<YourClass,bool>>' e 'Expression<Func<YourClass,bool>>'

Per questo io uso un metodo di estensione per questo. Nel tuo esempio si sarebbe simile a questa:  .Where(FindInZip(12345).Or(NameStartsWith("XYZ")).And(PostedOnOrAfter(DateTime.Now)).

Al posto di:

.Where(FindInZip(12345) || NameStartsWith("XYZ") && (PostedOnOrAfter(DateTime.Now)).

Esempio di espressione:

private Expression<Func<Post,bool>> PostedOnOrAfter(DateTime cutoffDate)
{
      return post => post.PostedOn >= cutoffDate;
};

metodo di estensione:

public  static  class PredicateExtensions
{
     ///  <summary>
     /// Begin an expression chain
     ///  </summary>
     ///  <typeparam id="T""></typeparam>
     ///  <param id="value"">Default return value if the chanin is ended early</param>
     ///  <returns>A lambda expression stub</returns>
     public  static Expression<Func<T,  bool>> Begin<T>(bool value =  false)
    {
         if (value)
             return parameter =>  true;  //value cannot be used in place of true/false

         return parameter =>  false;
    }

     public  static Expression<Func<T,  bool>> And<T>(this Expression<Func<T,  bool>> left,
        Expression<Func<T,  bool>> right)
    {
         return CombineLambdas(left, right, ExpressionType.AndAlso);
    }

     public  static Expression<Func<T,  bool>> Or<T>(this Expression<Func<T,  bool>> left, Expression<Func<T,  bool>> right)
    {
         return CombineLambdas(left, right, ExpressionType.OrElse);
    }

     #region private

     private  static Expression<Func<T,  bool>> CombineLambdas<T>(this Expression<Func<T,  bool>> left,
        Expression<Func<T,  bool>> right, ExpressionType expressionType)
    {
         //Remove expressions created with Begin<T>()
         if (IsExpressionBodyConstant(left))
             return (right);

        ParameterExpression p = left.Parameters[0];

        SubstituteParameterVisitor visitor =  new SubstituteParameterVisitor();
        visitor.Sub[right.Parameters[0]] = p;

        Expression body = Expression.MakeBinary(expressionType, left.Body, visitor.Visit(right.Body));
         return Expression.Lambda<Func<T,  bool>>(body, p);
    }

     private  static  bool IsExpressionBodyConstant<T>(Expression<Func<T,  bool>> left)
    {
         return left.Body.NodeType == ExpressionType.Constant;
    }

     internal  class SubstituteParameterVisitor : ExpressionVisitor
    {
         public Dictionary<Expression, Expression> Sub =  new Dictionary<Expression, Expression>();

         protected  override Expression VisitParameter(ParameterExpression node)
        {
            Expression newValue;
             if (Sub.TryGetValue(node,  out newValue))
            {
                 return newValue;
            }
             return node;
        }
    }

     #endregion
} 

Una davvero buon articolo su LINQ Query by Estensione Espressioni. Anche l'origine del metodo di estensione che uso.

https://www.red-gate.com/simple-talk/dotnet/net-framework/giving-clarity-to-linq-queries-by-extending-expressions/

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top