System.Linq.Dynamic.Core icon indicating copy to clipboard operation
System.Linq.Dynamic.Core copied to clipboard

Problem with Query over JObject, Filter with SubProperty not returning the correct value

Open luizfbicalho opened this issue 4 years ago • 7 comments

I created this console application to reproduce my problem

       static void Main(string[] args)
        {
            var obj = new object[] {
               new { Id= 36,
                Descricao= "Responsiva",
                Tipo=new  { Id= "1" },TipoId=  "1" ,
                Custo= 160
              },
              new {
                Id= 31,
                Descricao= "Log",
                Tipo= new { Id= "1" },TipoId=  "1" ,
                Custo= 0
              },
              new {
                Id= 32,
                Descricao= "Multi-Idioma",
                Tipo= new { Id= "2" },TipoId=  "2" ,
                Custo= 2
              },
              new {
                Id= 33,
                Descricao= "Tela Administrativa",
                Tipo= new { Id= "2" },TipoId=  "2" ,
                Custo= 8
              }
            };


            var json = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
            var dados = Newtonsoft.Json.JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JObject[]>(json);

            var retorno = dados.AsQueryable()
                               .Select("new (  string(Id) as Id, string(Descricao) as Descricao,new (  string(Id) as Id)  as Tipo, string(TipoId) as TipoId, string(Custo) as Custo)");
            retorno = retorno.OrderBy("Descricao asc");

            var retorno1 = retorno.Where("(TipoId = @0)", "1");
            var retorno2 = retorno.Where("(Tipo.Id = @0)", "1");

            Console.WriteLine(retorno.Count());
            Console.WriteLine(retorno1.Count());
            Console.WriteLine(retorno2.Count());
            Console.ReadLine();

        }

If you look at the results , the count should be the same, but for some reason, retorno2 is filtering for the Root Id of the object, and not the Tipo property and Id SubProperty

luizfbicalho avatar Aug 24 '20 12:08 luizfbicalho

Hello @luizfbicalho ,

It's possible that new ( string(Id) as Id) as Tipo should be instead new ( string(Tipo.Id) as Id) as Tipo ?

var retorno = dados.AsQueryable()
				   .Select("new (  string(Id) as Id, string(Descricao) as Descricao,new (  string(Tipo.Id) as Id)  as Tipo, string(TipoId) as TipoId, string(Custo) as Custo)");

Once we change it, it now filters the Tipo.Id as you initially wanted.

Best Regards,

Jon


Performance Libraries context.BulkInsert(list, options => options.BatchSize = 1000); Entity Framework ExtensionsEntity Framework ClassicBulk OperationsDapper Plus

Runtime Evaluation Eval.Execute("x + y", new {x = 1, y = 2}); // return 3 C# Eval FunctionSQL Eval Function

JonathanMagnan avatar Aug 28 '20 04:08 JonathanMagnan

Thanks @JonathanMagnan , I'll try that.

luizfbicalho avatar Aug 28 '20 12:08 luizfbicalho

One thing before closing this If I remove the line

.Select("new ( string(Id) as Id, string(Descricao) as Descricao,new ( string(Id) as Id) as Tipo, string(TipoId) as TipoId, string(Custo) as Custo)");

Shoudn't it work inspecting the JObject? I changed it to dynamic and received this

"Target object is not an ExpandoObject"

luizfbicalho avatar Aug 28 '20 14:08 luizfbicalho

Unfortunately, the library is not 100% compatible with JObject.

So the answer is no for the moment.

We might want to improve the code later to try to support more but there is no plan for now.

JonathanMagnan avatar Aug 28 '20 15:08 JonathanMagnan

I didn't look deeply inside of the code, but is there some kind of translator that I could implement, for example There is a object to where that translates the objects properties to the where string, and I could implement one translator of JObject to where, and even one from dynamic to Where

I'm not sure if I was clear about it

luizfbicalho avatar Aug 29 '20 15:08 luizfbicalho

I'm not exactly sure but this answer might help you: https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/410#issuecomment-678825890

This is possible for you to add some method that will be recognized by the library

JonathanMagnan avatar Aug 31 '20 13:08 JonathanMagnan

I was thinking about something more transparent, I mean something like this

IReader<T>{GetProperties(T obj).......}

and in Where("t=>t.Property==@0",X) would use the DI or some array of readers kind of the way newtonsoft.json has the converters.

I imagine that this is a huge change in the way that this works today, if so, I'm just pointing some ideas to help in the future

luizfbicalho avatar Aug 31 '20 13:08 luizfbicalho

I was more thinking in this direction:

Use it:

var array = JArray.Parse(@"[
        {
            ""first"": 1,
            ""City"": ""Paris"",
            ""third"": ""test""
        },
        {
            ""first"": 2,
            ""City"": ""New York"",
            ""third"": ""abc""
        }]");

        var results = array.Where("City == @0", "Paris").ToDynamicArray();
        foreach (var result in results)
        {
            Console.WriteLine(result.first);
        }

Logic

public static JArray Where(this JArray source, JsonParsingConfig config, string predicate, params object?[] args)
    {
        Check.NotNull(source);

        var array = new JArray();

        if (source.Count == 0)
        {
            return array;
        }

        // Convert the JArray to a dynamic object array / queryable.
        var enumerable = source.ToDynamicJsonClassArray(config.DynamicJsonClassOptions).AsQueryable();

        // Apply the where clause.
        var results = enumerable.Where(config, predicate, args);

        // Convert the dynamic results back to a JArray.
        foreach (var result in results)
        {
            array.Add(JObject.FromObject(result));
        }

        return array;
    }

StefH avatar Apr 08 '24 12:04 StefH

Will be solved by https://github.com/zzzprojects/System.Linq.Dynamic.Core/issues/789

StefH avatar Apr 13 '24 19:04 StefH