nested "Any" in is not working in dynamic linq

1.2k views Asked by At

We offer our users the ability to build their own queries on the data. To make this work we use the Dynamic LINQ library. This construct is giving us problems: We have a collection of persons which we query as follows:

( DossierItems.Any( DossierFiles.Any())) && ( FirstName.Contains( \"h\" ) )

This results in the error stating that:

No property or field 'FirstName' exists in type 'DossierItem'

Which is entirely correct: FirstName is a property of person. The closing parenthesis after the second “any” is somehow missed.

( FirstName.Contains( \"h\" ) ) && ( DossierItems.Any( DossierFiles.Any() ) )

The statement above works just fine, but we can’t control the order in which the predicates are entered.

Is there a way in which to modify the nested Any parts so the play nice with any following predicates?

This is the stack trace produced by Dynamic Linq when parsing the dynamic linq string:

   at System.Linq.Dynamic.ExpressionParser.ParseMemberAccess(Type type, Expression instance)
   at System.Linq.Dynamic.ExpressionParser.ParseIdentifier()
   at System.Linq.Dynamic.ExpressionParser.ParsePrimaryStart()
   at System.Linq.Dynamic.ExpressionParser.ParsePrimary()
   at System.Linq.Dynamic.ExpressionParser.ParseUnary()
   at System.Linq.Dynamic.ExpressionParser.ParseMultiplicative()
   at System.Linq.Dynamic.ExpressionParser.ParseAdditive()
   at System.Linq.Dynamic.ExpressionParser.ParseComparison()
   at System.Linq.Dynamic.ExpressionParser.ParseLogicalAnd()
   at System.Linq.Dynamic.ExpressionParser.ParseLogicalOr()
   at System.Linq.Dynamic.ExpressionParser.ParseExpression()
   at System.Linq.Dynamic.ExpressionParser.ParseParenExpression()
   at System.Linq.Dynamic.ExpressionParser.ParsePrimaryStart()
   at System.Linq.Dynamic.ExpressionParser.ParsePrimary()
   at System.Linq.Dynamic.ExpressionParser.ParseUnary()
   at System.Linq.Dynamic.ExpressionParser.ParseMultiplicative()
   at System.Linq.Dynamic.ExpressionParser.ParseAdditive()
   at System.Linq.Dynamic.ExpressionParser.ParseComparison()
   at System.Linq.Dynamic.ExpressionParser.ParseLogicalAnd()
   at System.Linq.Dynamic.ExpressionParser.ParseLogicalOr()
   at System.Linq.Dynamic.ExpressionParser.ParseExpression()
   at System.Linq.Dynamic.ExpressionParser.Parse(Type resultType)
   at System.Linq.Dynamic.DynamicExpression.ParseLambda(ParameterExpression[] parameters, Type resultType, String expression, Object[] values)
   at System.Linq.Dynamic.DynamicExpression.ParseLambda(Type itType, Type resultType, String expression, Object[] values)
   at System.Linq.Dynamic.DynamicQueryable.Where(IQueryable source, String predicate, Object[] values)
   at Repositories.Base.Repository`1.ApplyQuery(IQueryable`1 entities, Guid queryId)
   at Search.SearchQueryResult.RunQuery[T](IQueryable`1 entities, IRepository`1 repository)

Code sample for repro

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System.Linq.Dynamic;

namespace ClassLibrary1 {

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {
        // arrange
        var allAs = new List<A>();

        // act
        // pass
        var actual = allAs.Where("(Name = \"\")&&(Bs.Any(Cs.Any()))");

        // fail
        var actual = allAs.Where("(Bs.Any(Cs.Any()))&&(Name = \"\")");        
    }
}

public class A
{
    public string Name { get; set; }
    public IList<B> Bs
    {
        get { return bs; }
        set { bs = value; }
    }
    private IList<B> bs = new List<B>(0);
}

public class B
{
    public A A { get; set; }
    public IList<C> Cs
    {
        get { return cs; }
        set { cs = value; }
    }
    private IList<C> cs = new List<C>(0);
}

public class C
{
    public B B { get; set; }
}

}

3

There are 3 answers

1
finite8 On

If it's of any help to anyone, I figured out the cause. It is an issue in the original source and i have just done a pull request on Github with a fix for it.

https://github.com/kahanu/System.Linq.Dynamic/pull/68

This issue will occur with any nested linq method call. Solved by replacing a variable in their parser with a Stack to get the right Type to be popped when stepping out of a method call.

0
Stef Heyenrath On

Already works fine in my branch from this library : System.Linq.Dynamic.Core

1
niklr On

It seems to work with the original source code which can be downloaded here: https://msdn.microsoft.com/en-US/vstudio/bb894665.aspx

Just use the CSharpSamples\LinqSamples\DynamicQuery\DynamicQuery\Dynamic.cs class instead of the System.Linq.Dynamic NuGet package.