ExpressionTreeVisualizer
ExpressionTreeVisualizer copied to clipboard
KeyNotFoundException when opening visualizer
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.
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.
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.