3

I want to go from

        var selectData = (from i in data
                          where i.Name == "Bob1"
                          select i);

To

        var selectData = (from i in data
                          select i).Where("Name==Bob1");

I've tried various approaches (AsQueryable, Where<SomeData>), but can't get the second form to compile.

I do not have a good grasp on C#'s generic extension methods. The <Tsource> doesn't make sense to me so that could be the problem. Also, I do not understand why I can type .Where() when intellisense only displays .Where<> (a generic). I expect to see a second Where without the generic symbol...alas I do not.

Class

public class SomeData
{
    public string Name { get; set; }
    public string Address { get; set; }
}

UPDATE
There appears to be some confusion as to how Where() can be used that may well be my fault. Please see a related question. Based off this answer, the property name in the where clause is perfectly legal. I need the property to remain a string. If that means dynamic LINQ is required, then so be it...that's what I need.

Community
  • 1
  • 1
P.Brian.Mackey
  • 43,228
  • 68
  • 238
  • 348
  • This seems similar to another post. Maybe this will help [LINQ - dynamic WHERE clause?][1] [1]: http://stackoverflow.com/questions/848415/linq-dynamic-where-clause – Pranav Shah Mar 21 '12 at 18:06
  • You need to dynamically query your property names? Can you confirm this? – payo Mar 21 '12 at 18:07
  • I still don't understand. If you're looking for how to do dynamic Linq, is the answer you already cited on the [other question you asked](http://stackoverflow.com/questions/9505189/dynamically-generate-linq-queries) not already the answer to this question too? – Paul Ruane Mar 21 '12 at 18:10
  • @Paul Ruane - I am trying to get the other answer to work. I assumed Dynamic LINQ was just a part of .NET OOB functionality. And that I was doing something syntactically incorrect. Obviously that is not the case. – P.Brian.Mackey Mar 21 '12 at 18:12
  • @P.Brian.Mackey: I've had a quick read around. Appears it may be a [separate download](http://stackoverflow.com/questions/3991108/where-can-i-find-the-system-linq-dynamic-dll#3991141). – Paul Ruane Mar 21 '12 at 18:16
  • @PaulRuane yes, it's a separate download – phoog Mar 21 '12 at 18:41

5 Answers5

2
var selectData = (from i in data
                  select i).Where(datum => datum.Name == "Bob1");

The Where method takes a delegate, not a string, so you need to pass in a delegate or lambda.

Edit: based upon your comment to one of the other answers, you will need to use Reflection to make the property value lookup dynamic.

Edit: looks like you need to download the source code for the Dynamic Linq library separately.

Community
  • 1
  • 1
Paul Ruane
  • 37,459
  • 12
  • 63
  • 82
  • If that is the case, please explain this other related answer: http://stackoverflow.com/questions/9505189/dynamically-generate-linq-queries – P.Brian.Mackey Mar 21 '12 at 18:04
  • 1
    @P.Brian.Mackey: That uses dynamic Linq. You could certainly explore that but that is not what you asked in your question. – Paul Ruane Mar 21 '12 at 18:05
  • So am I to assume dynamic LINQ is an add-on that I need to download and reference? – P.Brian.Mackey Mar 21 '12 at 18:10
  • @P.Brian.Mackey: I have no idea, I've never used it. I'm reading the link posted in the accepted answer in your other question as we speak. – Paul Ruane Mar 21 '12 at 18:11
2

With all your help I have managed to get the conversion to function.

  1. Install Dynamic LINQ (I used NUGET. Search online for System.Linq.Dynamic)
  2. Add using System.Linq.Dynamic
  3. Query should be of the form

        var selectData = (from i in data
                          select i).AsQueryable().Where("Name = @0","Bob1");//@0 is called an identifier.  "Name = Bob1" straight up fails.
    
  4. Install ScottGU's C# sample library...it helps. (VB) (Original Post)

P.Brian.Mackey
  • 43,228
  • 68
  • 238
  • 348
1

UPDATE

I misunderstood the question initially; the solution to the problem is to download Dynamic Linq and reference it. I'll leave my answer below it addresses the side questions you asked about generic extension methods.


var selectData = (from i in data 
    select i).Where(d => d.Name=="Bob1");

But why not this:

var selectData = data.Where(d => d.Name=="Bob1");

Regarding the "non-generic" version of where, there is no such thing. In the calls above, the type parameter of the generic method is implicit; it has been inferred by the compiler, which compiles the call exactly as it would compile this:

var selectData = data.Where<SomeData>(d => d.Name=="Bob1");

Perhaps a sketch implementation of the Where method will help reduce your confusion about the TSource parameter:

public static IEnumerable<TSource> Where(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
{
    foreach (TSource item in source)
        if (predicate(item))
            yield return item;
}

TSource is the element type of the sequence you are querying. It's also the element type of the result sequence.

The compiler needs to know the type for at least two reasons:

First, we need to call a function on each element to determine whether to include it in the result sequence. The compiler has to know that the referent of the predicate parameter can safely accept a parameter of type TSource.

The second reason is somewhat trivial in this case; item has to be assignment compatible with TSource because it is used in a yield return statement. Of course it is compatible, because it is of the same type.

phoog
  • 42,068
  • 6
  • 79
  • 117
  • I can't do that. I need the property as a string. – P.Brian.Mackey Mar 21 '12 at 18:02
  • What do you mean "need the property as a string"? The property *is* a string. Do you mean you want to select the value of the property as a string? If so, the value will be "Bob1". If you describe the desired value of `selectData` perhaps it will help clarify. – phoog Mar 21 '12 at 18:06
  • @P.Brian.Mackey do you mean that you want to specify the property being queried at run-time, by passing its name to the method as a string? – phoog Mar 21 '12 at 18:14
  • @P.Brian.Mackey ok! In that case, using extension method syntax won't help. Query comprehension syntax is semantically equivalent to extension method syntax; the compiler translates the query into extension methods anyway. – phoog Mar 21 '12 at 18:19
  • @P.Brian.Mackey I added some material about generics that will, I hope, help clear up some of the confusion you mentioned about the `TSource` parameter. – phoog Mar 21 '12 at 18:29
0

I believe this is what you're looking for:

http://www.albahari.com/nutshell/predicatebuilder.aspx

Example

IQueryable<Product> SearchProducts (params string[] keywords)
{
  var predicate = PredicateBuilder.False<Product>();

  foreach (string keyword in keywords)
  {
    string temp = keyword;
    predicate = predicate.Or (p => p.Description.Contains (temp));
  }
  return dataContext.Products.Where (predicate);
}

Source code

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;

public static class PredicateBuilder
{
  public static Expression<Func<T, bool>> True<T> ()  { return f => true;  }
  public static Expression<Func<T, bool>> False<T> () { return f => false; }

  public static Expression<Func<T, bool>> Or<T> (this Expression<Func<T, bool>> expr1,
                                                      Expression<Func<T, bool>> expr2)
  {
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
          (Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
  }

  public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
                                                       Expression<Func<T, bool>> expr2)
  {
    var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
    return Expression.Lambda<Func<T, bool>>
          (Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
  }
}
msigman
  • 4,474
  • 2
  • 20
  • 32
0

Here's some simple code using an Expression tree to do what you want. This will only work for Property == "..." queries against that particular type. You can of course modify this and make it as generic as you need.

    public void Test()
    {
        List<SomeData> data = new List<SomeData>();
        data.Add(new SomeData("Mark", "Ledgewood Drive"));
        data.Add(new SomeData("Tim", "Sumpter Drive"));
        data.Add(new SomeData("Sean", "Leroy Drive"));
        data.Add(new SomeData("Bob", "Wilmington Road"));
        data.Add(new SomeData("Sean", "Sunset Blvd"));

        List<SomeData> result = data.Where(BuildExpression("Name", "Mark")).ToList();
        List<SomeData> result2 = data.Where(BuildExpression("Address", "Wilmington Road")).ToList();
    }

    private Func<SomeData, bool> BuildExpression(string propertyName, string value)
    {
        ParameterExpression pe = Expression.Parameter(typeof(SomeData), "someData");
        Expression left = Expression.Property(pe, propertyName);
        Expression right = Expression.Constant(value);
        BinaryExpression binary = Expression.Equal(left, right);
        Expression<Func<SomeData, bool>> lambda = Expression.Lambda<Func<SomeData, bool>>(binary, pe);
        return lambda.Compile();
    }
mdm20
  • 4,475
  • 2
  • 22
  • 24