تمديد Iqueryable أين () كما أو بدلا من العلاقة

StackOverflow https://stackoverflow.com/questions/930677

  •  06-09-2019
  •  | 
  •  

سؤال

أنا أستخدم أساليب الإرشاد الخاصة بي ل IQueryable <> لإنشاء استعلامات قابل للتعديل مثل FindAld (). Findinzip (12345) .namestartswith ("XYZ"). orderbyhowiiantit () وما إلى ذلك بعد ذلك على التنفيذ المؤجل يخلق استعلام واحد بناء على بلدي سلسلة طرق التمديد.

المشكلة مع هذا، هي أن كل مكان في سلسلة التمديد (FindXyz، Findinzip وما إلى ذلك) سوف تتحد دائما كما والذي يعني أنني لا أستطيع فعل شيء مثل هذا:

Findall (). Firstnamestartswith ("X"). orlastnamestartswith ("z") لأنني لا أعرف كيف يمكنني حقن أو في طريقة منفصلة فيها.

أي فكرة كيف يمكنني حل هذا؟


إضافي؛ حتى الآن أفهم كيفية سلسلة التعبيرات كما أو إذا قمت بلفها (مثل الترجمة (Firstnamestartswith ("A"). ترسامستارزواي ("z"). orderby (..))

ما أحاول القيام به رغم أنه أكثر تعقيدا قليلا (وتسجيل المساسخ لا يساعد هنا ..) أريد أن أحيل لاحقا IQueryable للوصول بشكل أساسي إلى الظروف التي تم إنشاؤها قبل ذلك دون الحاجة إلى لفها لإنشاء أو بين هم.

نظرا لأن كل طريقة تمديد إرجاع IQueryable <> أفهم أنه يجب أن يكون لها معرفة حول الوضع الحالي لظروف الاستعلام في مكان ما، مما يدفعني إلى الاعتقاد بأنه يجب أن يكون هناك بعض الطرق الآلية أو إنشاء أو عبر كل ما قبل الظروف دون الحاجة إلى التفاف ما تريد أو.

هل كانت مفيدة؟

المحلول

أفترض أن الأجزاء المختلفة من الاستعلام معروفة فقط في وقت التشغيل، أي لا يمكنك فقط استخدامها || في where...

خيار كسول واحد هو Concat - ولكن هذا يميل إلى أن يؤدي إلى ضعف Tsql وغيرها؛ ومع ذلك، فإنني أميل إلى أن أميل إلى كتابة مخصص Expressions بدلا من ذلك. يعتمد النهج الذي يتعين اتخاذه على ما يدعمه المزود، حيث يدعم LinQ-To-SQL خيارات مختلفة إلى EF (على سبيل المثال) - والتي لها تأثير حقيقي هنا (نظرا لعدم استخدام التعبيرات الفرعية مع EF). هل تستطيع أن تخبرنا بذلك؟


إليك بعض الكود الذي يجب أن يعمل مع LinQ-To-SQL؛ إذا قمت ببناء مجموعة (أو قائمة، والاتصال .ToArray()) من التعبيرات، يجب أن تعمل بشكل جيد؛ على سبيل المثال هو LinQ-to-cossss، ولكن يجب أن لا تزال تعمل:

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

نصائح أخرى

يستخدم PredicateBuilder<T>. وبعد ربما ما تريد.

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

في عالم مثالي أنا شخصيا أعتقد || و && سيكون المشغلون الأكثر بساطة وقابلة للقراءة. ومع ذلك لن يترجم.

المشغل "||" لا يمكن تطبيقها على المعاملات من النوع "Expression<Func<YourClass,bool>>' و 'Expression<Func<YourClass,bool>>'

لذلك يمكنني استخدام طريقة تمديد لهذا. في مثالك، يبدو الأمر كذلك: .Where(FindInZip(12345).Or(NameStartsWith("XYZ")).And(PostedOnOrAfter(DateTime.Now)).

بدلا من:

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

مثال التعبير:

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

طريقة التمديد:

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
} 

مقالة جيدة حقا حول استفسارات LINQ عن طريق تمديد التعبيرات. أيضا مصدر طريقة التمديد التي أستخدمها.

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top