tags:

views:

1694

answers:

3

Hello,

I have extension method:

public static IQueryable<TResult> WithFieldLike<TResult>(
   this IQueryable<TResult> query,
   Func<TResult, string> field,
   string value)
{
   Expression<Func<TResult, bool>> expr = 
       trans => field(trans).Contains(value);
   return query.Where(expr);
}

I need change parameter field to type: Expression>. Will be something like.

public static IQueryable<TResult> WithFieldLike<TResult>(
   this IQueryable<TResult> query,
   Expression<Func<TResult, string>> field,
   string value)
{
   Expression<Func<TResult, bool>> expr = ???
   return query.Where(expr);
}

The call of this method is:

var query7 = query.WithFieldLike(trans => trans.DeviceModelNumber, "ber_3");

How should I build the "expr" in this case? Please help.

A: 

Use Compile to get the lambda back out:

Expression<Func<TResult, bool>> expr = 
   trans => field.Compile()(trans).Contains(value);

Edit: Whoops - my air compiler failed me. After compiling, you get the delegate. But, you still need to call it to get the string to call Contains.

Mark Brackett
Will be compilation error:'System.Func<TResult,string>' does not contain a definition for 'Contains' and no extension method 'Contains' accepting a first argument of type 'System.Func<TResult,string>' could be found (are you missing a using directive or an assembly reference?)
Maxs
@Maxs: Yeah - I kind of screwed up the whole "converting it to another expression" part. Point stands though - use Compile to get the lambda, and use it as normal.
Mark Brackett
+4  A: 

Deconstruct field and create a new expression, something like this:

var expr = Expression.Lambda<Func<TResult, bool>> (
    Expression.Call (field.Body, typeof (string).GetMethod ("Contains"),
        Expression.Constant (value)), field.Parameters) ;

(edited as per Maxs's refinement in comments)

Anton Tykhyy
The point is that I can't build "Contains" expression. How to build "Equals" expression I know.
Maxs
Oops, sorry — edited answer
Anton Tykhyy
Thanks. This works perfectly.
Maxs
The final result is: var expr1 = Expression.Lambda<Func<TResult, bool>>( Expression.Call(field.Body, typeof(string).GetMethod("Contains"), Expression.Constant(value)) , field.Parameters);
Maxs
Oh, sheesh. I didn't see Expression.Lambda<>() in `ildasm` ^ω^
Anton Tykhyy
+2  A: 

You'll need to use Expression.Invoke; something like (untested):

public static IQueryable<TResult> WithFieldLike<TResult>(
   this IQueryable<TResult> query,
   Expression<Func<TResult, string>> field,
   string value)
{
    var param = Expression.Parameter(typeof(TResult), "x");
    var expr = Expression.Lambda<Func<TResult, bool>>(
        Expression.Call(Expression.Invoke(field, param),
            "Contains", null, Expression.Constant(value)), param);

    return query.Where(expr);
}

(edit: fixed)

Marc Gravell
Thanks. This works too. But generates expression incompatible with ADO.NET Data Services call.
Maxs
Gotta love the Law of Leaky Abstractions... it works with some but not all ;-p
Marc Gravell