5

I'm try'n to create a dynamic query tool using System.Linq.Expressions.Expression (WPF/c#4.0) It runs against an OData Service.

So far, all is working as long as I limit the conditions to build in options like Equal(..), GreaterThan(..) etc. There seems to be no build in contains/Like condition so I tried building my own. There are a handful of articles already out there. One I tried is How to create a System.Linq.Expressions.Expression for Like?.

Now if I use the above solution, the resulting where expression is

whereCallExpression = {Convert([10000]).Expand("A,B").Where(clt => MyLike(clt.LastName, "te"))}'

which is nice, but wrong, since it does not translate into a valid Odata query.

If I use condition 'Equal', the result is

whereCallExpression = {Convert([10000]).Expand("A,B").Where(clt => (clt.LastName == "te"))}

which results in the OData query

results = {http://.../Clients()?$filter=LastName eq 'te'&$expand=A,B} 

and is working as expected.

Am I doing something wrong with the implementation of the solution, or can it not be used with OData?

It should transfer to something like ...?$filter=substringof('te', LastName ) eq true

Any solution on how to fix this?

Regards

Andreas

PS, I implemented the solution extension in a static class, all I changed is the name of the called method from 'Like' to 'MyLike' Also, since the code used to build the expressions is working with any of the build-in condition, I assume, for now that part is ok. I can post parts of it if needed

Community
  • 1
  • 1
Andreas
  • 681
  • 1
  • 8
  • 16

1 Answers1

1

OData currently doesn't support the "like" operator at all. So no matter what you do on the client, the URL produced has not way of expressing that. The substringof is supported and the client LINQ provider should generate it when you use string.Contains method in your filter (Where) expression.

To get the expression generated by a C# compiler you can do something like this:

IQueryable<string> source = new List<string>().AsQueryable();
IQueryable<string> result = from item in source where item.Contains("John") select item;
Console.WriteLine(result.Expression.ToString());

Basically any IQueryable has a property Expression which contains the expression tree for the query to run. Some LINQ providers might change the expression tree somewhat from the original one created by the compiler, but most should leave it close to the original.

Vitek Karas MSFT
  • 13,130
  • 1
  • 34
  • 30
  • Thanx for your answer @Vitek Karas MSFT, I don't really understand your point doh. You say there is a 'Contains' option I should use, But if I check in 'System.Linq.Expressions.Expression' I see no contains, only Equal (for strings) . Let me restate my problem. I can query the service using 'var res = from c in svc.clients where c.LastName.CONTAINS("..") select c;' which will generate a working query, the same for Startswith, Endswith. What I need is to create the query dynamically, at run-time, via Expression tree. Is it possible I use a wrong, or outdated lib? – Andreas Aug 02 '11 at 18:35
  • I see - the Contains is just a method call, so in your expression tree generate an Expression.Call for the string.Contains method with the right parameters. The LINQ provider will recognize this pattern and will treat it as the "substringof" in the URL. – Vitek Karas MSFT Aug 02 '11 at 18:48
  • You can create a LINQ expression with C#, so just write the "from ..." and assign the result to Expression typed variable. Then you can inspect it in debugger for example to see what expression tree C# compiler builds, so that you can simulate it in your own code. – Vitek Karas MSFT Aug 02 '11 at 18:49
  • Uh,this sounds good. Unfortunately, I have no idea what you mean with or how to "Assign the result of an "from c in b.." to Expression Type". Mind giving a small example? – Andreas Aug 03 '11 at 05:53
  • I edited the asnwer to include the sample. There's also a way to do this by assigning a lambda to Expression but the IQueryable approach is a bit easier :-) – Vitek Karas MSFT Aug 03 '11 at 10:02
  • This is nice, took me a while to see your point, thanx for sticking with me. – Andreas Aug 04 '11 at 00:59
  • For completeness, this is what I ended up with to create the 'Contains' Expression – Andreas Aug 04 '11 at 01:01
  • 2
    `pe = Expression.Parameter(typeof(myClient), "clt"); PropertyInfo propertyInfo = typeof(myClient).GetProperty("PropertyFieldName"); MemberExpression m = Expression.MakeMemberAccess(pe, propertyInfo); ConstantExpression c = Expression.Constant("SearchFrase", typeof(string)); MethodInfo mi = typeof(string).GetMethod("Contains", new Type[] { typeof(string) }); e1 = exp.Expression.Call(m, mi, c);` – Andreas Aug 04 '11 at 01:02