Frage

ich meine eigenen Erweiterungsmethoden von IQueryable bin mit <> verkettbar Anfragen wie FindAll (). FindInZip (12345) .NameStartsWith ( „XYZ“). OrderByHowIWantIt () usw., die dann auf verzögerte Ausführung zu erstellen, eine einzelne Abfrage erstellt basiert auf meiner Erweiterungsmethoden Kette.

Das Problem dabei ist aber, dass alles Wo in der Verlängerungskettchen (FindXYZ, FindInZip etc.) wird immer kombinieren und was bedeutet, ich kann so etwas nicht tun:

FindAll (). FirstNameStartsWith ( "X"). OrLastNameStartsWith ( "Z"), weil ich weiß nicht, wie ich das oder in einem separaten injizieren kann Wo Verfahren.

Jede Idee, wie ich dieses Problem lösen kann?


Zusatz; Bis jetzt verstehe ich, wie Ketten Ausdrücke wie Oder, wenn ich sie wickeln (z CompileAsOr (FirstNameStartsWith ( "A"). LastNameStartsWith ( "Z"). SortiertNach (..))

Was ich versuche zu tun, obwohl ist etwas komplizierter (und PredicateBuilder hilft hier nicht ..), dass ich möchte ein später IQueryable im Grunde die Zugriff Wo Bedingungen, die vor eingerichtet wurden, ohne sie zu wickeln zu erstellen die Oder zwischen ihnen.

Da jede Erweiterung Methode gibt IQueryable <> Ich verstehe, dass es das Wissen über den aktuellen Stand der Abfragebedingungen irgendwo haben sollte, die mich führt zu glauben, dass es einige automatisiert sein oder eine oder das Erstellen über alle früheren Wo Bedingungen ohne mit wickeln, was Sie wollen ODER-verknüpft.

War es hilfreich?

Lösung

Ich gehe davon aus, die verschiedenen Teile der Abfrage werden nur zur Laufzeit bekannt, das heißt Sie können nicht nur || in einem where verwenden ...

Eine faule Option ist Concat - aber dies neigt dazu, schlechte TSQL usw. zu führen; aber ich neige dazu, individuellen Expressions geneigt zu sein, anstatt zu schreiben. Der Ansatz zu nehmen, hängt ab, was der Anbieter ist, wie LINQ to SQL verschiedene Optionen zu EF (zum Beispiel) unterstützt - die hier eine echte Wirkung hat (da Sie nicht Unterausdrücke mit EF verwenden können). Können Sie uns sagen, was?


Hier ist ein Code, der mit LINQ-to-SQL arbeiten sollte; wenn Sie ein Array (oder eine Liste, und rufen .ToArray()) bauen von Ausdrücken, sollte es funktionieren; Beispiel hierfür ist LINQ-to-Objects, soll aber immer noch funktionieren:

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

Andere Tipps

Verwenden Sie PredicateBuilder<T> . Es ist wahrscheinlich das, was Sie wollen.

    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 einer idealen Welt denke ich persönlich || und && Betreiber die einfachste und lesbar sein würden. Allerdings wird es nicht kompiliert werden.

  

Operator '||' kann nicht auf Operanden vom Typ ‚Expression<Func<YourClass,bool>>‘ und ‚Expression<Func<YourClass,bool>>

angewandt werden

Deshalb verwende ich eine Erweiterungsmethode für diese. In Ihrem Beispiel würde es wie folgt aussehen:  .Where(FindInZip(12345).Or(NameStartsWith("XYZ")).And(PostedOnOrAfter(DateTime.Now)).

Statt:

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

Expression Beispiel:

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

Erweiterungsmethode:

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
} 

Ein wirklich guter Artikel über LINQ-Abfragen durch die Ausdrücke erweitern. Auch die Quelle der Erweiterung Methode, die ich benutze.

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

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top