문제

나는 findall (). findinzip (12345) .namestartSwith ( "xyz")와 같은 체인 가능한 쿼리를 만들기 위해 iqueryable <>의 내 확장 방법을 사용하고 있습니다. 확장 방법 체인.

그러나 이것의 문제점은 확장 체인 (findxyz, findinzip 등)의 모든 위치가 항상 결합 될 것이라는 점입니다.

findall (). FirstNamestArtSwith ( "x"). OrlastNamestArtSwith ( "Z")는 어떻게 또는 별도의 방법을 주입 할 수 있는지 모르기 때문에.

내가 이것을 어떻게 해결할 수 있는지 아십니까?


추가의; 지금까지 나는 표현을 랩핑하는 방법을 이해하거나 (예 : compileasor ( "a"). LastnamestArtswith ( "z"). Orderby (..))를 이해합니다.

내가하려고하는 것은 약간 더 복잡합니다 (그리고 술어 빌더는 여기서 도움이되지 않습니다.) 나중에 iqueryable이 기본적으로 기본적으로 설정된 조건에 액세스 할 수 있기를 원합니다. 그들을.

각 확장 방법이 iQueryable을 반환함에 따라 쿼리 조건의 현재 상태에 대한 지식이 있어야한다는 것을 이해합니다. 당신이 원하는 것.

도움이 되었습니까?

해결책

쿼리의 다른 부분이 런타임에만 알려져 있다고 가정합니다. 즉, 사용할 수 없습니다. || 안에 where...

게으른 옵션 중 하나입니다 Concat - 그러나 이것은 TSQL이 열악한 경향이 있습니다. 그러나 나는 관습을 작성하는 경향이 있습니다. Expression대신 s. LINQ-to-SQL은 EF (예 : EF와 함께 하위 표현을 사용할 수 없기 때문에)에 대한 진정한 영향을 미치는 EF (예 : EF)에 대한 다양한 옵션을 지원하기 때문에 공급자가 무엇인지에 달려 있습니다. 당신은 우리에게 무엇을 말할 수 있습니까?


다음은 LINQ-to-SQL과 함께 작동 해야하는 코드입니다. 배열 (또는 목록을 작성하고 .ToArray()) 표현의 경우 잘 작동해야합니다. 예를 들어 LINQ-to-Objects이지만 여전히 작동해야합니다.

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

Expression example:

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-plarity-to-linq-queries-s-extending-expressions/

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top