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; }
}
}
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.