Converting Where from LINQ Clause to Dynamic LINQ
I want to go from
var selectData = (from i in data
where i.Name == "Bob1"
select i);
For
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 am not very good at general C # extension methods. <Tsource>
doesn't make sense to me, so this might be a problem. Also, I don't understand why I can print .Where()
when intellisense only displays .Where<>
(general). I expect to see the second one Where
without a common symbol ... alas, I don't.
Class
public class SomeData
{
public string Name { get; set; }
public string Address { get; set; }
}
UPDATE
There seems to be some confusion as to how Where () can be used, which could very well be my mistake. See related question. Based on 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 let it be ... what I need.
With all your help, I was able to get the conversion to function.
- Install dynamic LINQ (I used NUGET. Internet search for System.Linq.Dynamic)
- Add to
using System.Linq.Dynamic
-
The request 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.
-
Install ScottGU C # Sample Library ... it helps. ( VB ) ( Original post )
var selectData = (from i in data
select i).Where(datum => datum.Name == "Bob1");
The method Where
accepts a delegate, not a string, so you need to pass a delegate or lambda.
Edit: Based on your comment on one of the other answers, you would need to use Reflection in order to dynamically change the property value dynamically.
Edit: It looks like you need to download the source code for the Dynamic Linq library separately.
UPDATE
I misunderstood the question; the solution to the problem is to download Dynamic Linq and reference it. I'll leave my answer below, which addresses the side questions you asked regarding common 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-tribal" version where there is no such thing. In the above calls, the type parameter of the generic method is implicit; it was output by the compiler, which compiles the call exactly the same way it would compile this:
var selectData = data.Where<SomeData>(d => d.Name=="Bob1");
Perhaps implementing a method sketch Where
will help reduce your confusion about the parameter TSource
:
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 requesting. It is 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 item to determine whether to include it in the result sequence. The compiler needs to know that the parameter referent predicate
can safely accept a parameter of type TSource.
The second reason in this case is somewhat trivial; item
must be an assignment that is compatible with TSource
because it is used in a statement yield return
. Of course it is compatible because it is of the same type.
I believe this is what you are 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
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);
}
}
Here's some simple code using an expression tree to do what you want. This will only work for properties == ...... requests of that particular type. You can of course change this and make it generic as you like.
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();
}