ExpressionTreeVisualizer icon indicating copy to clipboard operation
ExpressionTreeVisualizer copied to clipboard

KeyNotFoundException when opening visualizer

Open bart-degreed opened this issue 3 years ago • 2 comments

In response to here.

Thanks, v1.7.115 fixes the exception. It's a bit hard to repro this because we dynamically build expressions. I've tried to create a repro using LINQ code, but the compiler optimizes away the casts, so it results in a different tree.

The best I can offer to repro at the moment is:

  • Clone https://github.com/json-api-dotnet/JsonApiDotNetCore/tree/net50 (note the non-default branch name)
  • Add EnableUnsafeBinaryFormatterSerialization to JsonApiDotNetCoreExample project and set as startup
  • Add a breakpoint to EntityFrameworkCoreRepository.ApplyQueryLayer
  • Run run-docker-postgres.ps1, which starts a PostgreSQL database in docker container
  • Run and open http://localhost:14140/api/v1/todoItems?filter=and(startsWith(description,'T'),equals(priority,'Low'),not(equals(owner,null)),not(equals(assignee,null))) in a browser.

This should hit the breakpoint and enable you to activate the visualizer.

bart-degreed avatar Jul 15 '21 11:07 bart-degreed

Thanks for opening this. I'll try to repro when I get a chance.

Could I trouble you to choose the "Factory methods" formatter from within the visualizer on one of these expressions, and provide the output? It generates C# with the factory method calls needed to produce a similar expression. That should be a far more straightforward repro path.

zspitz avatar Jul 15 '21 11:07 zspitz

Yes sure, I wasn't able to do that earlier, due to the crash. See output below. However, I'm not sure how to translate the "not implemented" part.

// using static System.Linq.Expressions.Expression

var todoItem = Parameter(
    typeof(TodoItem),
    "todoItem"
);

Call(
    typeof(Queryable).GetMethod("Where", new[] { typeof(IQueryable<TodoItem>), typeof(Expression<Func<TodoItem, bool>>) }),
    --
-- Not implemented - NodeType: Extension not implemented.
--,
    Quote(
        Lambda(
            AndAlso(
                AndAlso(
                    AndAlso(
                        Call(
                            MakeMemberAccess(todoItem,
                                typeof(TodoItem).GetProperty("Description")
                            ),
                            typeof(string).GetMethod("StartsWith", new[] { typeof(string) }),
                            MakeMemberAccess(
                                Call(
                                    typeof(Tuple).GetMethod("Create", 1, new[] { typeof(string) }),
                                    Constant("T")
                                ),
                                typeof(Tuple<string>).GetProperty("Item1")
                            )
                        ),
                        Equal(
                            MakeMemberAccess(todoItem,
                                typeof(TodoItem).GetProperty("Priority")
                            ),
                            MakeMemberAccess(
                                Call(
                                    typeof(Tuple).GetMethod("Create", 1, new[] { typeof(TodoItemPriority) }),
                                    Constant(TodoItemPriority.Low)
                                ),
                                typeof(Tuple<TodoItemPriority>).GetProperty("Item1")
                            )
                        )
                    ),
                    Not(
                        Equal(
                            MakeMemberAccess(todoItem,
                                typeof(TodoItem).GetProperty("Owner")
                            ),
                            Convert(
                                Constant(null),
                                typeof(Person)
                            )
                        )
                    )
                ),
                Not(
                    Equal(
                        MakeMemberAccess(todoItem,
                            typeof(TodoItem).GetProperty("Assignee")
                        ),
                        Convert(
                            Constant(null),
                            typeof(Person)
                        )
                    )
                )
            ),
            todoItem
        )
    )
)

The Tuple.Create().Item1 calls basically represent constants, but this makes EF Core parameterize the produced SQL.

The models used are in the models directory.

bart-degreed avatar Jul 15 '21 12:07 bart-degreed