Microsoft Developer Network > Forums Home > Archived Forums Forums > LINQ Project General > Dynamic conditions: How to achieve multiple "OR" conditions with LINQ?
Ask a questionAsk a question
 

AnswerDynamic conditions: How to achieve multiple "OR" conditions with LINQ?

  • Wednesday, July 26, 2006 3:27 AMmarcioesteves Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    A common use for database querying in a page is to allow users to enter a list of keywords for searching. i.e. "Word1 Word2 Word3" along with some settings options where he can pick something like: [All words], [Any word], [Exact Phrase].

    I used that approach to write a learning tool with LINQ.

    Exact Prase was easily achieved by something like:

    from q in context.MyView
    where q.MySearchField.Contains(TextBox.Text)
    select q

    which translated to

    select * from MyView where MySearchField like '%xxxxxxx%'

    IndexOf also worked and translated to CHARINDEX instead of LIKE. I also tested StartsWith() and EndsWith(). They all worked great.

    Likewise I was able to perform the [All words] and liked the way LINQ handled that:

    IQueryable<MyView> filter = context.MyView;
    foreach (string word in TextBox.Text.Split(' '))
        filter = filter.Where(f => f.MySearchField.Contains(word));

    Which translated to

    select * from MyView
    where    MySearchField like '%Word 1%'
    and        MySearchField like '%Word 2%'
    etc...

    However I don't seem to be able to find a way to handle the [Any word] case in an elegant way. I'd like LINQ to generate ORs instead of ANDs.

    I could achieve the same final result using Union() but of course that had a terrible performance. It would be nice if could pass a parameter indicating "OR" or "AND" to each .Where() call.

    Is there a way to do that?
    Thanks

Answers

  • Wednesday, July 26, 2006 10:27 PMTomas PetricekMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    Hi,
    I found two possible ways for writing this, however one of them (the one that looks simplier) generates really ugly SQL code, so I don't recomend using it. The wrong way is to use Queryable.Union to merge several queries (one query for every keyword). This approach is similar to using Queryable.Where to merge multiple filters, but the resulting SQL code looks bad:

    NorthwindDataContext db = new NorthwindDataContext();
    var q1 = Queryable.Where(db.Products, (p) => p.ProductName.Contains("Chef"
    ));
    var q2 = Queryable.Where(db.Products, (p) => p.ProductName.Contains("Sir"
    ));
    var filter = Queryable.
    Union(q1, q2);
    var q = from prod in
    filter
     
    select new { prod.ProductName };

    Is translated to:

    SELECT [t3].[ProductName] FROM (
        SELECT [t2].[ProductName] FROM (
            SELECT [t0].[ProductName] FROM [Products] AS [t0]
               
    WHERE [t0].[ProductName] LIKE @p0
            UNION SELECT [t1].[ProductName] FROM [Products] AS [t1] 
                WHERE [t1].[ProductName] LIKE @p1) AS [t2]
        ) AS [t3]

    To get better results, you can build whole expression tree used for filtering at runtime (using the method described by Matt Warren in his blog). The code for building your query will look like this:

    NorthwindDataContext db = new NorthwindDataContext();
    ParameterExpression p = Expression.Parameter(typeof(Product), "p"
    );

    // build as many conditions as you need
    Expression
    t1 = Expression.CallVirtual(
      
    typeof(string).GetMethod("Contains"
    ),
       Expression.Property(p, typeof(Product).GetProperty("ProductName"
    )),
       new Expression[] { Expression.Constant("Chef"
    ) });
    Expression t2 = Expression.CallVirtual(
      
    typeof(string).GetMethod("Contains"
    ),
       Expression.Property(p, typeof(Product).GetProperty("ProductName"
    )),
       new Expression[] { Expression.Constant("Sir"
    ) });

    // merge conditions using Expression.Or
    var
    test = Expression.Lambda<Func<Product, bool>>
     
    (Expression.Or(t1, t2), new ParameterExpression
    [] { p });
    var filter = Queryable.Where(db.
    Products, test);

    var q = from prod in filter
       select new { prod.ProductName };

    This is a bit more difficult to generate, but the resulting SQL code is what you would expect.

All Replies

  • Wednesday, July 26, 2006 10:27 PMTomas PetricekMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    Hi,
    I found two possible ways for writing this, however one of them (the one that looks simplier) generates really ugly SQL code, so I don't recomend using it. The wrong way is to use Queryable.Union to merge several queries (one query for every keyword). This approach is similar to using Queryable.Where to merge multiple filters, but the resulting SQL code looks bad:

    NorthwindDataContext db = new NorthwindDataContext();
    var q1 = Queryable.Where(db.Products, (p) => p.ProductName.Contains("Chef"
    ));
    var q2 = Queryable.Where(db.Products, (p) => p.ProductName.Contains("Sir"
    ));
    var filter = Queryable.
    Union(q1, q2);
    var q = from prod in
    filter
     
    select new { prod.ProductName };

    Is translated to:

    SELECT [t3].[ProductName] FROM (
        SELECT [t2].[ProductName] FROM (
            SELECT [t0].[ProductName] FROM [Products] AS [t0]
               
    WHERE [t0].[ProductName] LIKE @p0
            UNION SELECT [t1].[ProductName] FROM [Products] AS [t1] 
                WHERE [t1].[ProductName] LIKE @p1) AS [t2]
        ) AS [t3]

    To get better results, you can build whole expression tree used for filtering at runtime (using the method described by Matt Warren in his blog). The code for building your query will look like this:

    NorthwindDataContext db = new NorthwindDataContext();
    ParameterExpression p = Expression.Parameter(typeof(Product), "p"
    );

    // build as many conditions as you need
    Expression
    t1 = Expression.CallVirtual(
      
    typeof(string).GetMethod("Contains"
    ),
       Expression.Property(p, typeof(Product).GetProperty("ProductName"
    )),
       new Expression[] { Expression.Constant("Chef"
    ) });
    Expression t2 = Expression.CallVirtual(
      
    typeof(string).GetMethod("Contains"
    ),
       Expression.Property(p, typeof(Product).GetProperty("ProductName"
    )),
       new Expression[] { Expression.Constant("Sir"
    ) });

    // merge conditions using Expression.Or
    var
    test = Expression.Lambda<Func<Product, bool>>
     
    (Expression.Or(t1, t2), new ParameterExpression
    [] { p });
    var filter = Queryable.Where(db.
    Products, test);

    var q = from prod in filter
       select new { prod.ProductName };

    This is a bit more difficult to generate, but the resulting SQL code is what you would expect.

  • Wednesday, July 26, 2006 11:43 PMTomas PetricekMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    One more thing to mention :-).
    I already expermineted with LINQ and I wrote set of classes that enable some other features that are not directly supported by LINQ (for example you can re-use part of query witout copy&paste across multiple queries). This project shows how extensions to LINQ conversion can be implemented.

    I had some free time, so I tried to implement what you asked for (ContainsAny and ContainsAll methods that can be translated to SQL), so you can write:

    // Select products, that conain one of values from array in their name
    var q1 = from p in db.Products.ToExpandable()
       
    where p.ProductName.ContainsAny("Sir", "Chef")
       
    select new { p.ProductName };

    // Select products, that conain all of values from array in their name
    var q2 = from p in db.Products.ToExpandable()
       
    where p.ProductName.ContainsAll("Gu", "Ca")
       
    select new { p.ProductName };

    Some links:

    PS: However, I hope that something like this may be available in future versions of LINQ without any extensions...

  • Thursday, July 27, 2006 2:09 AMmarcioesteves Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    The first approach is the one I had already tried and as you said it produces poor SQL.

    The second one was great, I had to do some massage to make it work for a list of words but your example showed the concept perfectly.

    Thanks Tomas.

    Here's how I got passed the multiple Expression.Or calls:

    // Several expressions, one for each condition
    List<Expression> expList = new List<Expression>();
    foreach (string word in wordsWithForOrMoreLetters)
    expList.Add
    (
    Expression.CallVirtual
    (
    typeof(string).GetMethod("Contains"),
    Expression.Property(p, typeof(MyView).GetProperty("MyField")),
    new Expression[] { Expression.Constant(word) }
    )
    );

    // Create first expression
    Expression<Func<MyView
    , bool>> condition = Expression.Lambda<Func<MyView, bool>>
    (
    expList[0],
    new ParameterExpression[] { p }
    );

    // Merge using Expression.Or
    for (int i = 1; i < expList.Count; i++)
    condition = Expression.Lambda<Func<
    MyView, bool>>
    (
    Expression.Or(condition.Body, expList[ i ]),
    new ParameterExpression[] { p }
    );

  • Saturday, July 29, 2006 12:09 PMJoe AlbahariMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    An ideal solution would exist if lambdas were composable, ie:

    Expression <Func <MyView, bool>> predicate = f => f.MySearchField.Contains (word);

    and then:

    foreach (string word in TextBox.Text.Split(' '))
      predicate = f => predicate (f) || f.MySearchField.Contains (word);

    then:

    var result = MyView.Where (predicate);


    Unfortunately this isn't possible.  Here's an alternative that is possible:

    var predicate = PredicateBuilder.Get (<MyView>());

    foreach (string word in TextBox.Text.Split(' '))
      predicate = predicate.Or (f => f.MySearchField.Contains (word));

    var result = MyView.Where (predicate);


    -- if PredicateBuilder is defined as follows:



        static class PredicateBuilder
        {
            public static Expression<Func<T, bool>> Get<T> () { return null; }

            public static Expression<Func<T, bool>> Get<T> (this Expression<Func<T, bool>> predicate)
            {
                return predicate;
            }

            public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> or)
            {
                if (expr == null) return or;
                Replace (or, or.Parameters[0], expr.Parameters[0]);
                return Expression.Lambda<Func<T, bool>> (Expression.Or (expr.Body, or.Body), expr.Parameters);
            }

            public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> and)
            {
                if (expr == null) return and;
                Replace (and, and.Parameters[0], expr.Parameters[0]);
                return Expression.Lambda<Func<T, bool>> (Expression.And (expr.Body, and.Body), expr.Parameters);
            }

            static void Replace (object instance, object old, object replacement)
            {
                for (Type t = instance.GetType (); t != null; t = t.BaseType)
                    foreach (FieldInfo fi in t.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
                    {
                        object val = fi.GetValue (instance);
                        if (val != null && val.GetType ().Assembly == typeof (Expression).Assembly)
                            if (object.ReferenceEquals (val, old))
                                fi.SetValue (instance, replacement);
                            else
                                Replace (val, old, replacement);
                    }
            }
        }

    The Replace method is a tad grubby (divert your eyes :-)  There's probably a better way of doing this. It does, though, allow queries like this:

       var predicate = PredicateBuilder.Get<Customer> (c => c.Name.Contains ("A"));
       predicate = predicate.Or (c => c.Name.Contains ("B"));
       predicate = predicate.Or (c => c.Name.Contains ("C"));
       predicate = predicate.Or (c => c.Name.Contains ("X") && c.Age > 30);
       predicate = predicate.Or (c => c.Age > 50 && c.Age < 55);

       var query = dataContext.Customers.Where (predicate);

    The benefit is that each line can be wrapped around an if statement.

  • Monday, July 31, 2006 5:00 AMMatt Warren - MSFT Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    We're working on C# language syntax that will allow you to compose expression trees at runtime. However, Joe's solution looks good to me. :-)

     

  • Friday, October 27, 2006 1:25 AMJoe AlbahariMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Here's an updated version that uses Expression.Invoke instead of reflection:


    static class PredicateBuilder
    {
        public static Expression<Func<T, bool>> Make<T> () { return null; }

        public static Expression<Func<T, bool>> Make<T> (this Expression<Func<T, bool>> predicate)
        {
            return predicate;
        }

        public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> or)
        {
            if (expr == null) return or;
            var invokedExpr = Expression.Invoke (or, expr.Parameters.Cast<Expression> ());
            return Expression.Lambda<Func<T, bool>> (Expression.Or (expr.Body, invokedExpr), expr.Parameters);
        }

        public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> and)
        {
            if (expr == null) return and;
            var invokedExpr = Expression.Invoke (and, expr.Parameters.Cast<Expression> ());
            return Expression.Lambda<Func<T, bool>> (Expression.And (expr.Body, invokedExpr), expr.Parameters);
        }
    }




        var predicate = PredicateBuilder.Make<Customer> (c => c.Name.Contains ("A"));
        predicate = predicate.Or (c => c.Name.Contains ("B"));
        predicate = predicate.Or (c => c.Name.Contains ("C"));
        predicate = predicate.Or (c => c.Name.Contains ("X") && c.Age > 30);
        predicate = predicate.Or (c => c.Age > 50 && c.Age < 55);

        var query = customers.Where (predicate);
  • Wednesday, February 21, 2007 7:11 PMdoughboy Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Joe,

    Can you modify it to work with anonymous type?  With the current implementation, you have to declare the type beforehand.  But if you are just creating an anonymous type on the fly, can PredicateBuilder still work with the anonymous type?

     Joe Albahari wrote:
    Here's an updated version that uses Expression.Invoke instead of reflection:


    static class PredicateBuilder
    {
        public static Expression<Func<T, bool>> Make<T> () { return null; }

        public static Expression<Func<T, bool>> Make<T> (this Expression<Func<T, bool>> predicate)
        {
            return predicate;
        }

        public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> or)
        {
            if (expr == null) return or;
            var invokedExpr = Expression.Invoke (or, expr.Parameters.Cast<Expression> ());
            return Expression.Lambda<Func<T, bool>> (Expression.Or (expr.Body, invokedExpr), expr.Parameters);
        }

        public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> and)
        {
            if (expr == null) return and;
            var invokedExpr = Expression.Invoke (and, expr.Parameters.Cast<Expression> ());
            return Expression.Lambda<Func<T, bool>> (Expression.And (expr.Body, invokedExpr), expr.Parameters);
        }
    }




        var predicate = PredicateBuilder.Make<Customer> (c => c.Name.Contains ("A"));
        predicate = predicate.Or (c => c.Name.Contains ("B"));
        predicate = predicate.Or (c => c.Name.Contains ("C"));
        predicate = predicate.Or (c => c.Name.Contains ("X") && c.Age > 30);
        predicate = predicate.Or (c => c.Age > 50 && c.Age < 55);

        var query = customers.Where (predicate);

  • Thursday, February 22, 2007 12:01 AMJoe AlbahariMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
     doughboy wrote:

    Can you modify it to work with anonymous type?



    Sure: add this method to PredicateBuilder:

        public static Expression<Func<T, bool>> MakePredicate<T> (this IEnumerable<T> source)
        {
            return null;
        }

    Use it as follows:

        var query =
            from c in dataContext.Customers
            select new { Name = c.Name };

        var predicate = query.MakePredicate ();

        predicate = predicate.Or (c => c.Name == "Tom");
        predicate = predicate.Or (c => c.Name == "Harry");
        query = query.Where (predicate);


    Cheers

    Joe
  • Thursday, February 22, 2007 6:21 PMdoughboy Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Thanks Joe.

    A couple of questions...

    1. why IEnumerable<T>?  I would have thought IQueryable<T> instead but I don't really know what I'm talking about :)

    2. I don't know if this is the limitation of LINQ, well at least for now, but what if I wanted to build the where clause in a separate method?  What would the method signature look like? Given:

        var q =
        from c in ctx.Contact
        select new { ContactId = c.ContactId, FirstName = c.FirstName, LastName = c.LastName };
    
        // is this possible?
        q = BuildWhere(q, criteria); // criteria is some object containing filter information
        ...
    
        private IQueryable<T> BuildWhere(IQueryable<T> source, FilterCriteria criteria)
        {
            var pred = source.MakePredicate(); // using your new method to work with anonymous type
            pred = pred.And(c => c.FirstName.StartsWith(criteria.FirstName)); // compile time error here...
            ...
            source = source.Where(pred);
            return source;
        }
    

    I am basically stuck because inside BuildWhere method, for example, the compiler complains that FirstName (from c.FirstName) is not defined.

    I hope it's clear what I am trying to accomplish but I am not sure whether it's even possible.

     Joe Albahari wrote:

     doughboy wrote:

    Can you modify it to work with anonymous type?



    Sure: add this method to PredicateBuilder:

        public static Expression<Func<T, bool>> MakePredicate<T> (this IEnumerable<T> source)
        {
            return null;
        }

    Use it as follows:

        var query =
            from c in dataContext.Customers
            select new { Name = c.Name };

        var predicate = query.MakePredicate ();

        predicate = predicate.Or (c => c.Name == "Tom");
        predicate = predicate.Or (c => c.Name == "Harry");
        query = query.Where (predicate);


    Cheers

    Joe
  • Sunday, February 25, 2007 10:57 AMJoe AlbahariMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    In answer to your first question, it doesn't matter whether it's IEnumerable<T> or IQueryable<T> - that method's sole interest is the T. IEnumerable<T> makes it slightly more versatile.

    On your second question: instead of projecting and then filtering, go the other way round - filter first then project. That will allow you to change the "BuildWhere" method so it will accept an IQueryable<Customer> instead of IQuerable<T>. Then it will compile.

    Joe
  • Tuesday, March 06, 2007 7:28 PMmsadekd Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
     doughboy wrote:

    Joe,

    Can you modify it to work with anonymous type?  With the current implementation, you have to declare the type beforehand.  But if you are just creating an anonymous type on the fly, can PredicateBuilder still work with the anonymous type?

     Joe Albahari wrote:
    Here's an updated version that uses Expression.Invoke instead of reflection:


    static class PredicateBuilder
    {
        public static Expression<Func<T, bool>> Make<T> () { return null; }

        public static Expression<Func<T, bool>> Make<T> (this Expression<Func<T, bool>> predicate)
        {
            return predicate;
        }

        public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> or)
        {
            if (expr == null) return or;
            var invokedExpr = Expression.Invoke (or, expr.Parameters.Cast<Expression> ());
            return Expression.Lambda<Func<T, bool>> (Expression.Or (expr.Body, invokedExpr), expr.Parameters);
        }

        public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr, Expression<Func<T, bool>> and)
        {
            if (expr == null) return and;
            var invokedExpr = Expression.Invoke (and, expr.Parameters.Cast<Expression> ());
            return Expression.Lambda<Func<T, bool>> (Expression.And (expr.Body, invokedExpr), expr.Parameters);
        }
    }




        var predicate = PredicateBuilder.Make<Customer> (c => c.Name.Contains ("A"));
        predicate = predicate.Or (c => c.Name.Contains ("B"));
        predicate = predicate.Or (c => c.Name.Contains ("C"));
        predicate = predicate.Or (c => c.Name.Contains ("X") && c.Age > 30);
        predicate = predicate.Or (c => c.Age > 50 && c.Age < 55);

        var query = customers.Where (predicate);

    but i guess the ToString(); will have "invoke" inside, which is not the perfect solution, myself i went through revisiting all the expressions to replace the parameters of the second lambda with ones of the first. and i suggest that you change the visitor class to public cause it can be quite usefull...
  • Friday, March 16, 2007 4:51 PMRoger - Fenêtre Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Hi Matt,

    For me this is the only thing really missing in Dlinq so if it's added that would be great! I would hate to go back to direct sql or use the to complicated Lamba stuff for all search forms. I could not find this in the March CTP examples ;( Is there a place with examples for "simple expression tree building"?

    Tnxs,

    Roger

  • Monday, May 28, 2007 4:27 PMdterrie Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Joe,

     

    I've been studying how LINQ might replace the stringbuilder-based method of building a dynamic SQL statement. I've inherited an app that enables users to select multiple values from multiple lists and combine them using any combination of AND/OR/NOT. These conditions are stored in a table from which the WHERE clause is constructed on demand. Literally dozens of criteria can be selected, producing a WHERE clause only a T-SQL aficionado could love.

     

    Your PredicateBuilder class appears to handle most needs I foresee, but a few remain, though perhaps only because I don't understand LINQ well enough. The first problem is the classic need to control precedence:

     

    A or (B and C) vs. (A or B) and C.

     

    SELECT ID

    FROM MyTable

    WHERE (Color IN ('Red','Blue') OR Style = 'Modern') AND Price < 10000

     

    vs.

     

    SELECT ID

    FROM MyTable

    WHERE Color IN ('Red','Blue') OR (Style = 'Modern' AND Price < 10000)

     

    The second problem is employing set operators. How to I add UNION, INTERSECT, and EXCEPT to this mix dynamically (predicate.except...)? It is common for a user to ask 'Give me all items in set A, except those in set B', etc.

     

    One final comment for the LINQ team folks: As you battle for the hearts and minds of C# and database developers (who craft those dreaded sprocs for which too many C# guys seem to have contempt), you have done yourselves a screaming disservice by generating SQL that reads 'SELECT * FROM MyTable...' when projection is not used. Every DB guy older than the age of 5 knows that this is a cardinal sin. While you can simply say, well then use projection to carve out only the data you need, introducing this bad practice into the midst of folks too many of which know much less about writing good T-SQL than they think they do is dangerous. Please update LINQ to write out all of the field names in the table if projection is not used. You also need to enable users to 'rip' stored procedures into C#/LINQ as a way to 1) make DBs comfortable that the right questions are being asked (are those sales figures accurate?) 2) enable DB developers to better assist C# guys, and 3) give both a better understanding of how to build real-world LINQ queries. It appears LINQ has some great features that are difficult to achieve in T-SQL unless you are very good at it, but job 1 should be insuring that current results are accurately replicated (yes, I know you can call a sproc in LINQ, but every C# guy on the planet thinks he knows better and will try to take the training wheels off day 1). Sorry for the rant.

     

    Finally, thanks for your work Joe.

  • Friday, August 31, 2007 7:06 AM-Q Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    using Joe's predicate builder, the following code used to work in the 2006 May CTP

     

    var products = db.Products;

    var predicate = products.MakePredicate();

     

    foreach(int productId in productIds)

    {

    predicate = predicate.Or(p => p.Id == productId);

    }

     

    foreach(Product prod in products.Where(predicate))

    {

    \\... do something ...

    }

     

    However in Orcas beta 2, the code needs to be modified to:

     

    foreach(int productId in productIds)

    {

    int tmp = productId;

    predicate = predicate.Or(p => p.Id == tmp);

    }

     

    without the local scope tmp variable, only the value of the last element in the productIds collection will appear in the predicate. not sure if this is a bug or a feature of the beta 2.

  • Friday, August 31, 2007 8:12 AMJoe AlbahariMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    The need for the temporary variable is a consequence of expression-based lambda closures using the l-value rather than the r-value. This feature was introduced in Beta 1.

    Anders has suggested that in the final release, expressions may be composable, alleviating the need for PredicateBuilder, although the need for temporary variables in such a loop will remain (in fact without PredicateBuilder, you will need two temporary variables to construct such an expression).

    Here's how I anticipate your code will look in the final C# 3.0 release:

    string[] keywords = { "tea", "coffee" };
    Expression <Func<Products, bool>> predicate = p => false;

    foreach (string keyword in keywords)
    {
        Expression <Func<Products, bool>> tempPred = predicate;
        string tempKeyword = keyword;
        predicate = p => tempPred (p) || p.ProductName.Contains (tempKeyword);
    }

    Products.Where (predicate).Dump();

    A C# operator that forced use of the r-value might help, although we could be running out of symbols Wink

    Joe
  • Friday, September 14, 2007 11:32 AMEnigmaOX Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    How could you implenet this on somthing liket this

    public class Foo {
        public int X { get; set; }
        public string Y { get; set; }
    }

    public IQueryable<Foo> GetFoos(int x) {
        IQueryable<Foo> q =
            from q in db.Foos
            where q.X < x
            select new Foo { X = q.X, Y = q.Y };

          q = q.where (predicate);

    return q;
    }

     

  • Thursday, December 13, 2007 6:56 PMjshollax Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Joe, is there any update to this? Maybe a link to the new Predicate Builder replacement? I am running into a issue where predicate builder is just using the variable name instead of the actual value. This causes my query to fail...

     

    {c => ((((c.Area = value(PassDown_VS2008._Default+<>c__DisplayClass3).x.ToString().Trim()) And Invoke(c => (c.Area = value(PassDown_VS2008._Default+<>c__DisplayClass3).x.ToString().Trim()),c)) And Invoke(c => (c.Fab = value(PassDown_VS2008._Default+<>c__DisplayClass6).y.ToString().Trim()),c)) And Invoke(c => (c.Fab = value(PassDown_VS2008._Default+<>c__DisplayClass6).y.ToString().Trim()),c))}

     

     

    you can see here the x.ToString() is used not the actual value. I declared a local variable but in the beta version I have it dosen't seem to make a difference.

     

  • Friday, December 14, 2007 12:45 AMJoe AlbahariMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    PredicateBuilder is healthy and alive and works with RTM. We are using it in a production application.

    What is the specific query that's causing your problem?

    Joe
  • Monday, March 03, 2008 12:29 PMWalter Franssen Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Is this possible in VB.NET??

     

  • Saturday, March 21, 2009 4:58 AMJohn Aschenbrenner Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
    Hi all, I am trying to get PredicateBuilder to work with LINQ to Objects with no success.

    I can send a sample console app if that would help.

    Basically everything seems like it is working okay until I try to use the predicate I have built up in a where clause.

    Here is the line that is giving me the problem.

          public IQueryable<Employee> PrepareDataSource()  
          {  
             Expression<Func<Employee, bool>> predicate = PredicateBuilder.True<Employee>();  
     
             predicate = predicate.And(p => p.Country == "London");  
     
             Employees employees = new Employees();  
     
             IQueryable<Employee> results = employees.Where(predicate);  
             return results;  
          }  
     

    The employees.Where(predicate) is giving me the error - 

    Error 1 The type arguments for method 'System.Linq.Enumerable.Where<TSource>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,int,bool>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. C:\Users\john.aschenbrenner\Documents\Sand Box\LINQDyanamicSearchConsole\LINQDyanamicSearchConsole\Program.cs 23 41 LINQDyanamicSearchConsole


    Any help with this is greatly appreciated.

    Thanks,
    -ja

  • Saturday, March 21, 2009 12:08 PMJoe AlbahariMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    What type is Employees? Is it a collection of employee objects?

    PredicateBuilder is really designed for LINQ to SQL, although you can use it with LINQ to Objects if you add .AsQueryable().

    Try this:

    public IQueryable<Employee> PrepareDataSource()  
          {  
             Expression<Func<Employee, bool>> predicate = PredicateBuilder.True<Employee>();  
     
             predicate = predicate.And(p => p.Country == "London");  
     
             Employees employees = new Employees();  
     
             IQueryable<Employee> results = employees.AsQueryable().Where(predicate);  
             return results;  
          }  


    Joe

    Query databases interactively in LINQ - www.linqpad.net
  • Saturday, March 21, 2009 5:14 PMJohn Aschenbrenner Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Thanks Joe, I think that did the trick.

    Employees is an Object built by grabbing all the data in the Northwind Employees table and generating an xml by calling select for xml on the table.

    I then used XSLT to generate an Employees class which is a List of Employee.  

    class Employees : List<Employee>

    I often use XSLT for just this sort of thing as I can quickly create test data objects that way in a matter of minutes.

    I used to do a great deal of object manipulation using XSLT but am trying to change my stripes to use LINQ to Objects for that.

    I have not used LINQ to SQL at all yet as the applications that I have been working on use an XML file based system to store their data.  I have been trying to get the client to move toward using databases but that unfortuantely has not happened yet.  So, lately I have been just using LINQ to XML and LINQ to Objects.

    Much thanks,
    -ja
  • Saturday, March 21, 2009 8:00 PMJohn Aschenbrenner Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
     Okay that kind of works for me.  It seems that the call to PredicateBuilder only works with hard values, perhaps a code sample would explain it better.

    This is from the LINQ to Objects Console test program I have ginned up.

    #define does_not_work  
    using System;  
    using System.Collections.Generic;  
    using System.Linq;  
    using System.Linq.Expressions;  
    using System.Text;  
     
    namespace LINQDyanamicSearchConsole  
    {  
       class Program  
       {  
          static Employees employees = new Employees();  
          static string[] FirstNames = { "Nancy""Michael""Anne""Laura" };  
     
          static void Main(string[] args)  
          {
    #if does_work  
             IQueryable<Employee> foo = PrepareDataSource();  
     
             foreach (Employee item in foo)  
             {  
                Console.WriteLine("EmpId = {0} Name = {1} {2}", item.EmployeeID, item.FirstName, item.LastName);  
             }
    #endif  
     
    #if does_not_work  
             // this only returns the last object from the foreach in the WhereIn  
     
             foreach (Employee item in employees.WhereIn(FirstNames))  
             {  
                Console.WriteLine("EmpId = {0} Name = {1} {2}", item.EmployeeID, item.FirstName, item.LastName);  
             }
    #endif  
     
             Console.ReadLine();  
          }  
     
          public static IQueryable<Employee> PrepareDataSource()  
          {  
             Expression<Func<Employee, bool>> predicate = PredicateBuilder.False<Employee>();  
     
             /*
              * 
              * Both the below work
              * 
              */ 
             predicate = predicate.Or(p => p.FirstName == "Margaret");  
             predicate = predicate.Or(p => p.FirstName == "Steven");  
             predicate = predicate.Or(p => p.FirstName == "Robert");  
     
             predicate = predicate.Or(p => p.FirstName == FirstNames[0]);  
             predicate = predicate.Or(p => p.FirstName == FirstNames[1]);  
             predicate = predicate.Or(p => p.FirstName == FirstNames[2]);  
             predicate = predicate.Or(p => p.FirstName == FirstNames[3]);  
     
             IQueryable<Employee> results = employees.AsQueryable().Where(predicate);  
             return results;  
          }     
       }  
     
       internal static class EmployeeExtensions  
       {  
          public static IQueryable<Employee> WhereIn(this Employees emps, IEnumerable<string> predicateObjs)  
          {  
             Expression<Func<Employee, bool>> predicate = PredicateBuilder.False<Employee>();  
     
             foreach (string item in predicateObjs)  
             {  
                predicate = predicate.Or(p => p.FirstName == item);  
             }  
     
             IQueryable<Employee> retVal = emps.AsQueryable().Where(predicate);  
     
             return retVal;  
          }  
       }  
    }  
     


    Any ideas on how to make this work?

    Thanks much,
    -ja

  • Sunday, March 22, 2009 11:43 AMJoe AlbahariMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I'd say you've been bitten by the outer variable trap.

    Try changing the code in WhereIn as follows:


     foreach (string item in predicateObjs)  

       var temp = item;
       predicate = predicate.Or(p => p.FirstName == temp);  
    }  


    Bear in mind, though, that there's an easier way to solve your problem:
    http://social.msdn.microsoft.com/Forums/en-US/linqprojectgeneral/thread/b166af06-ccac-4807-b452-c546b5c1896f

    This will work both for LINQ to Objects and LINQ to SQL.

    Joe

    Query databases interactively in LINQ - www.linqpad.net
  • Sunday, March 22, 2009 9:07 PMJohn Aschenbrenner Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Thanks Joe,

    I did find the answer to my question answered on another thread by one fine Martin Honnen.  Thank you for the link to this other thread however.

    I must say this is maybe the second time I've even heard about the outer variable trap.  I will have to resaerch that a bit more.  I just could not easily let the LINQ to Objects PredicateBuilder problem that I had been trying to work out go idle.  Like a Yellow Lab with a new toy, I just couldn't easily let it go.

    -ja