views:

516

answers:

2

i want to create the following query in expression trees:

var test = from datarow in tempResults
                   where datarow.Field<String>("ColumnName") == "Column"
                   select datarow;

How do i create the expression : datarow.Field("ColumnName")?

i tried everything, i even got stuck on getting the MethodInfo of Field for the Expression.Call method. Field is an extention Method of DataRowExtentions.

Do i have to use Expression.Call() for this? How do i get the MethodInfo? is there a simplier way to do it ?

i tried :

ParameterExpression dataRow = Expression.Parameter(typeof(DataRowExtensions), "dataRow"); Expression left = Expression.Call(dataRow, typeof(DataRowExtensions).GetMethod("Field"));

but it doesn't work.


i want to create dynamic filters on the data inside IQueryable tempResults.

The user will check on checkboxes on the GUI that will add 'Where' expressions to the data in tempResults. when the user chooses "Column" i want to present the DataRows where ColumnName = "Column".

that is why i need to create the where expression. but i'm so stuck on the MethodInfo thing.... i tried this too:

MethodInfo FieldStringMethodInfo = typeof(DataRowExtensions).GetMethod("Field", BindingFlags.Public | BindingFlags.Static); it doesn't work too.

Is there other ways to do it ?

+2  A: 

Replacement answer following clarification in comments:

For successively building additional filters, you don't need expression trees; you can call .Where multiple times (as necessary, once per search term) - for example:

IEnumerable<DataRow> query = tempResults.AsEnumerable();
if(!string.IsNullOrEmpty(value1)) {
    query = query.Where(row => row.Field<string>("Col1") == value1);
}
if (!string.IsNullOrEmpty(value2)) {
    query = query.Where(row => row.Field<string>("Col2") == value2);
}

The only thing to watch is the "capture" issue; be sure not to re-use any of the value1, value2 etc - otherwise the last value will apply to earlier filters...


For an example of delegate combination (from comments) - note that I've dropped the DataTable aspect here purely to make the example shorter (it will work identically):

public static class Predicate {
    public static Func<T, bool> OrElse<T>(
            this Func<T, bool> lhs, Func<T, bool> rhs) {
        return lhs == null ? rhs : obj => lhs(obj) || rhs(obj);
    }
    public static Func<T, bool> AndAlso<T>(
            this Func<T, bool> lhs, Func<T, bool> rhs) {
        return lhs == null ? rhs : obj => lhs(obj) && rhs(obj);
    }
}
class Data {
    public string Color { get; set; }
}
class Program {
    static void Main() {
        bool redChecked = true, greenChecked = true; // from UI...
        List<Data> list = new List<Data>() {
            new Data { Color = "red"},
            new Data { Color = "blue"},
            new Data { Color = "green"},
        };
        Func<Data, bool> filter = null;
        if (redChecked) {
            filter = filter.OrElse(row => row.Color == "red");
        }
        if (greenChecked) {
            filter = filter.OrElse(row => row.Color == "green");
        }
        if (filter == null) filter = x => true; // wildcard

        var qry = list.Where(filter);

        foreach (var row in qry) {
            Console.WriteLine(row.Color);
        }
    }
}


(original answer)

Actually, that variant of LINQ won't use an expression tree... it will use a delegate; but you can build the tree and compile it if you really want... I'm not sure why you would, though. What do you want to do? I'll knock up an example...


Here you go; this uses an expression tree, but I can't think of a single good reason to do this, other than to prove that you can!

public static class MyExtensions
{
    public static IQueryable<TRow> Where<TRow, TValue>(
        this IQueryable<TRow> rows,
        string columnName, TValue value)
        where TRow : DataRow
    {
        var param = Expression.Parameter(typeof(TRow), "row");
        var fieldMethod = (from method in typeof(DataRowExtensions).GetMethods()
                           where method.Name == "Field"
                           && method.IsGenericMethod
                           let args = method.GetParameters()
                           where args.Length == 2
                           && args[1].ParameterType == typeof(string)
                           select method)
                           .Single()
                           .MakeGenericMethod(typeof(TValue));
        var body = Expression.Equal(
            Expression.Call(null,fieldMethod,
                param,
                Expression.Constant(columnName, typeof(string))),
            Expression.Constant(value, typeof(TValue))
        );
        var lambda = Expression.Lambda<Func<TRow, bool>>(body, param);
        return rows.Where(lambda);

    }
}
class Program
{
    static void Main(string[] args)
    {
        DataTable tempResults = new DataTable();
        tempResults.Columns.Add("ColumnName");
        tempResults.Rows.Add("foo");
        tempResults.Rows.Add("Column");

        var test = tempResults.AsEnumerable().AsQueryable()
                   .Where("ColumnName", "Column");
        Console.WriteLine(test.Count());

    }
}
Marc Gravell
i want to create dynamic filters on the data inside IQueryable<DataRow> tempResults.The user will check on checkboxes on the GUI that will add 'Where' expressions to the data in tempResults. when the user chooses "Column" i want to present the DataRows where ColumnName = "Column".that is why i need to create the where expression.but i'm so stuck on the MethodInfo thing.... i tried this too: MethodInfo FieldStringMethodInfo = typeof(DataRowExtensions).GetMethod("Field<string>", BindingFlags.Public | BindingFlags.Static);it doesn't work too.Is there other ways to do it ?
Rodniko
Ahh! You don't need expression trees for that... see update (top of)
Marc Gravell
Thank you marc ,the Asenumerable() example is good but i need a way to create it dynamically, because the user can choose:row => row.Field<string>("Col1") == value1 || row.Field<string>("Col1") == value2and next time:row => row.Field<string>("Col1") == value1 || row.Field<string>("Col1") == value2 || row.Field<string>("Col1") == value3that is why i need to compose it dynamically, i need to find a way to "Add whatever filters i want to a where clause" and only then execute the query.can i do that in in a simplier way then the expression tree example?
Rodniko
Can you give an example of what you mean?
Marc Gravell
the user chooses between 3 kinds of bloons (for example).he can choose blue and push "Submit" button. in that case i create a query with a where clause like this : where baloon == "blue".next time he checks the red,yellow and gray and push submit button.in that case i need to compose a query with a where clase like this:where baloon == "red" or baloon == "yellow" or baloon == "gray".thats why i can't do : query.Where(row => row.Field<string>("baloon") == "red");because in some cases it's not enough.do i need to use dynamic lambda expressions to do that ?
Rodniko
Well, *personally* I'd use a List of the colours and use Contains, i.e. `Where(row => colors.Contains(row.Field<SomeType>("Color")))`. The full `Expression` approach is only a *necessary* complication in the case of parser-based back-ends (LINQ-to-SQL, EF, etc); for this case (LINQ-to-Objects) I would happily use delegate combination; I'll try to update...
Marc Gravell
OMG!! could that be so simple??? using List ? it works like a charm! and this is the exact solution i was looking for - a way to dynamically create my own Where clause lambda expression that indicates : "include only the row that has one of the following values for a certain column"Thanks alot for your time, marc.
Rodniko
@Rodniko - `Contains` would be simpler... this was to discuss a specific case, but if you're happy...
Marc Gravell
A: 

Even i have the same case, but the search is case sensitive, i want a search with case insensitive result. Please help.

Divya