0

I'm making use of the dynamic-linq-query-builder library and am attempting to generate an executable Linq query from a JSON representation of the query. I don't believe that this library supports Dictionaries as it stands, so I am looking to add this functionality.

My JSON looks like this:

{
  "condition": "AND",
  "rules": [
    {
      "id": "Field_1",
      "field": "Data[\"Field_1\"].StringValue",
      "type": "string",
      "input": "text",
      "operator": "equal",
      "value": "test"
    }
  ],
  "valid": true
}

and I'm trying to generate a Linq query like this:

.Where(p => p.Data["Field_1"].StringValue == "test")

I'm really struggling with how to generate an System.Linq.Expressions.Expression which represents this, effectively hard-coded, dictionary item (the Data["Field_1"]) part of the tree.

I've read lots of articles, and looked at this StackOverflow question how-do-i-access-a-dictionary-item-using-linq-expressions but the solution is still evading me.

Can anyone point me in the right direction? The problem is manifesting in the BuildNestedExpression() method of this class.

Thanks in advance.

Update 13/12/19

I'm making some progress with this, but feel like I'm rapidly loosing my sanity. As previously mentioned, the problem manifests in the BuildNestedExpression() method, which currently does not support dictionary properties. I'm trying to add support for this.

I appreciate my code could be much improved (just trying to get it working initially), but my changes are in GitHub here

I've made a change to break out of the BuildNestedExpression() if I find a Dictionary into my new method BuildElementAccessExpression(). In here I'm simply trying to add any dictionary entries to the expression being built up for a property. I'm getting very confused in the process though (as you can probably tell by the commented out code).

I've added a quick unit test to illustrate the problem (Dictionary_Test).

I'll refrain from posting huge chunks of code on here since it's available in GitHub. Any help would be really great, don't think I'm going to solve this one on my own.

Bob
  • 372
  • 3
  • 13
  • So you're trying to build dynamic linq clauses? look here: https://stackoverflow.com/questions/848415/dynamic-where-clause-in-linq – Glenn van Acker Dec 06 '19 at 12:40
  • I recommend using LINQPad with its `Dump` method to examine compiler generated `Expression` trees (e.g. `Expression> f = p => p.Data["Field_1"].StringValue == "test";` then `f.Dump();`). – NetMage Dec 06 '19 at 19:13
  • If you do this, you'll discover that the `Dictionary` indexer is a property that takes a `TKey` parameter (in this case `string`) with a method named `get_Item(String key)` for get access. – NetMage Dec 06 '19 at 19:16
  • Thanks @NetMage that's really helpful and makes sense. I'll download LINQPad and see where that gets me. – Bob Dec 09 '19 at 13:09
  • @Bob Reading the SO question you linked, using property access on `Item` seems like it works as well (though not what the compiler does) per the [documentation](https://learn.microsoft.com/en-us/dotnet/api/system.linq.expressions.expression.property?view=netframework-4.8#System_Linq_Expressions_Expression_Property_System_Linq_Expressions_Expression_System_Reflection_PropertyInfo_System_Linq_Expressions_Expression___)? – NetMage Dec 09 '19 at 21:49
  • Testing with LINQPad, `Expression.Property` generates an `IndexExpression` instead of a call to `get_Item` but works fine when compiled. This support may be related to the VB availability of the `Item` property, though interestingly, the VB compiler creates calls to `get_Item` in both cases like the C# compiler. – NetMage Dec 09 '19 at 22:08

1 Answers1

0

I got there in the end with this problem. The process hurt my head a little, but the working code snippet to create the expression can be found below.

Hopefully this will save someone some time in the future.

Thanks for all your help @NetMage.

var propertyInfo = expression.Type.GetProperty("Data");
var propertyExpression = Expression.Property(expression, propertyInfo);

var dictionaryKeyValue = "dictionaryEntryKey";
var dictionaryKeyConst = Expression.Constant(dictionaryKeyValue);

var dictionary = propertyType.GetInterface("IDictionary`2");
PropertyInfo indexerProperty = dictionary.GetProperty("Item");

var indexExpression = Expression.MakeIndex(propertyExpression, indexerProperty, new[] { dictionaryKeyConst });

Bob
  • 372
  • 3
  • 13