1

I am using Microsoft's Dynamic Linq (System.Linq.Dynamic) library to generate some queries at run time. This has worked great for me, but for one specific scenario.

Simplified scenario - I am trying to query all claims which have some specific tags that the user has selected and whose Balance is greater than some number.

static void Main(string[] args)
    {
        var claims = new List<Claim>();
        claims.Add(new Claim { Balance = 100, Tags = new List<string> { "Blah", "Blah Blah" } });
        claims.Add(new Claim { Balance = 500, Tags = new List<string> { "Dummy Tag", "Dummy tag 1" } });

        // tags to be searched for
        var tags = new List<string> { "New", "Blah" };
        var parameters = new List<object>();
        parameters.Add(tags);

        var query = claims.AsQueryable().Where("Tags.Any(@0.Contains(outerIt)) AND Balance > 100", parameters.ToArray());
    }

public class Claim
{
    public decimal? Balance { get; set; }
    public List<string> Tags { get; set; }
}

This query throws an error:

An unhandled exception of type 'System.Linq.Dynamic.ParseException' occurred in System.Linq.Dynamic.dll Additional information: No property or field 'Balance' exists in type 'String'

Dynamic linq parser seems to try to find the Balance property on the Tag and not on the Claim object.

  • I have tried to play around with outerIt, innerIt, It keywords in Dynamic Linq but none of it seems to work.
  • Changing the sequence works, but that's not an option for me, since in the real application the filters, operators and patterns will be dynamic (configured by end user).
  • Boxing the conditions in brackets (), also doesn't help.
  • Workaround - create a simple contains condition for every Tag selected e.g. Tags.Contains("New") OR Tags.Contains("Blah") etc.. But in the real application it results in a really complex / bad query for each condition and kills the performance.

I might be missing something or this could be a bug in the library.

I would really appreciate if someone could help me with this.

Amanvir Mundra
  • 420
  • 1
  • 6
  • 20
  • where did you learn such syntax, any document links? – Lei Yang Apr 07 '17 at 07:32
  • This was my starting point: https://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library. Then there were other SO question from where I got references: http://stackoverflow.com/questions/37740409/c-sharp-dynamiclinq-where-clause-with-any – Amanvir Mundra Apr 07 '17 at 07:36
  • From what I understand, most of the keywords that works for Linq / EF should for dynamic linq with a bit of syntactic difference. – Amanvir Mundra Apr 07 '17 at 07:38
  • but in the doc links you provided, i did not see any syntax of `@n` to be a left value, it can only be a right value. – Lei Yang Apr 07 '17 at 07:39
  • @n to the left converts it into a SQL IN(values) operator. http://stackoverflow.com/questions/15633066/query-data-using-contains-keyword-in-dynamic-linq-in-c-sharp – Amanvir Mundra Apr 07 '17 at 07:44
  • @AmanvirSinghMundra I'll say that there is a bug in the original library – xanatos Apr 07 '17 at 07:45
  • I think `outerIt` is a `Claim` object, how can `List` Contain it? – Lei Yang Apr 07 '17 at 07:47
  • @LeiYang outerIt is a Tag object since there is an Any operator before it. Think of it as scope for lambda variable. If you try running the query without the Balance condition, then it works (with only the Tag condition). – Amanvir Mundra Apr 07 '17 at 07:51
  • you wrote 'Microsoft's Dynamic Linq', yes it was written by some Microsoft employee, but the library is not officially shipped with .net framework, is it? and, can you provide a more real world scenario why you must use such dynamic linq? – Lei Yang Apr 07 '17 at 07:53
  • It comes as a nuget package written by Microsoft and it is open sourced. Different flavors of it is used by a lot of vendors e.g. Kendo UI, Telerik (as far as I know). – Amanvir Mundra Apr 07 '17 at 07:58
  • @LeiYang It is the same comment I was going to do, **but**: https://www.nuget.org/packages/System.Linq.Dynamic/ ... Authors: Microsoft, description: This is the Microsoft assembly for the .Net 4.0 Dynamic language functionality. – xanatos Apr 07 '17 at 07:59
  • what problem do you want to solve(by C#, not linq)? – Lei Yang Apr 07 '17 at 08:02
  • @LeiYang that's mentioned in the question. User configures certain condition through a UI and my code generate a query for it dynamically. – Amanvir Mundra Apr 07 '17 at 08:04
  • which part of you query is input by the user at run time? if that counts, you should not omit the code. – Lei Yang Apr 07 '17 at 08:07

2 Answers2

1

Found a/the bug in ParseAggregate... The pushing of itouterIt and back doesn't work if there are multiple levels. The code supposes that the it and outerIt won't be changed by a third party before being reset (technically the code isn't reentrant). You can try with other variants of System.Linq.Dynamic (there are like two or three variants out of there). Probably some variants have already fixed it.

Or you can take the code from the linked site and recompile it inside your code (in the end the "original" System.Linq.Dynamic is a single cs file) and you can patch it like this:

Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos)
{
    // Change starts here
    var originalIt = it;
    var originalOuterIt = outerIt;
    // Change ends here

    outerIt = it;
    ParameterExpression innerIt = Expression.Parameter(elementType, elementType.Name);
    it = innerIt;
    Expression[] args = ParseArgumentList();

    // Change starts here
    it = originalIt;
    outerIt = originalOuterIt;
    // Change ends here

    MethodBase signature;
    if (FindMethod(typeof(IEnumerableSignatures), methodName, false, args, out signature) != 1)

I've already opened an Issue with the suggested bug fix in the github of the project.

xanatos
  • 109,618
  • 12
  • 197
  • 280
  • I'll patch the file with your fix and let you know if that solves it. Thanks a lot. – Amanvir Mundra Apr 07 '17 at 08:23
  • That works. Thanks man, you saved me weeks of toiling. I don't quite understand the nature of the fix in the whole context, so I am not sure what could be the impact of this fix. Would it make sense to log a defect on github repo, so that it is fixed in any upcoming releases? – Amanvir Mundra Apr 07 '17 at 09:04
  • @AmanvirSinghMundra 2 minutes ago I've made an update to my post: *I've already opened an Issue with the suggested bug fix in the github of the project.* – xanatos Apr 07 '17 at 09:05
  • Oops sorry you already done that, missed the last sentence. Thanks again – Amanvir Mundra Apr 07 '17 at 09:05
0

This seems to be working correctly in my version: System.Linq.Dynamic.Core

See the test here: https://github.com/StefH/System.Linq.Dynamic.Core/blob/master/test/System.Linq.Dynamic.Core.Tests/ComplexTests.cs#L19

Stef Heyenrath
  • 9,335
  • 12
  • 66
  • 121