问题
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.
回答1:
Found a/the bug in ParseAggregate... The pushing of it
→outerIt
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.
回答2:
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
来源:https://stackoverflow.com/questions/43272152/generate-dynamic-linq-query-using-outerit