简体   繁体   中英

Generate Dynamic Linq query using outerIt

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 eg 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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM