Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider doing property/navigation binding entirely from RelationalSqlTranslatingExpressionVisitor #31309

Open
Tracked by #34524
roji opened this issue Jul 19, 2023 · 1 comment

Comments

@roji
Copy link
Member

roji commented Jul 19, 2023

In our current query pipeline design, before RelationalQueryableMethodTranslatingEV sends a lambda body to translation in RelationalSqlTranslatingEV, it pre-visits the lambda (ExpandSharedTypeEntities, invoked from RemapLambdaBody). This pre-visitation pass "expands" property accesses into owned entities, effectively performing the binding at that point. When the lambda body later reaches SQL translation, the property access has already been dealt with.

It would be more natural to pass the lambda body as-is and do all translation inside RelationalSqlTranslatingEV, including binding of owned navigations etc. Aside from being more understandable, this would also reduce an additional visitation into all lambda bodies. I suspect this is done this way since RelationalSqlTranslatingEV currently doesn't know about the SelectExpression being processed; it only receives the lambda body as input.

Regular properties, on the other hand, are bound inside RelationalSqlTranslatingEV as expected. However, there as well, since the SelectExpression isn't available, it's not possible to produce the ColumnExpression at that point. Instead, EntityProjectionMapping contains a dictionary mapping all the entity's properties to ColumnExpressions; this dictionary is created in SelectExpression wherever an EntityProjectionMapping is instantiated, and is used from RelationalSqlTranslatingEV when binding a property.

The fix here would be for ProjectionBindingExpression to have a mapping between relational ITableBase and the TableReferenceExpressions of its SelectExpression. This would allow it to bind properties (and navigations) upon demand - given an IProperty, it would simply find the right ITable for it, get the TableReferenceExpression for that for the current SelectExpression, and construct a ColumnExpression over that.

Doing this would remove the property and owned navigation maps from EntityProjectionExpression, and hopefully simplify a lot of the logic for maintaining these.

/cc @maumar

@roji
Copy link
Member Author

roji commented Jul 31, 2023

Another argument against the pre-visitation expansion approach: when translating ExecuteUpdate, we remap the property selectors, which performs the expansion. This means that the lambda from remapping has already been expanded, and doesn't provide us with a reference to the containing entity type, which is what we need.

For example, for ExecuteUpdate(s => s.SetProperty(b => b.SomeComplexThing.Foo, 8) we get a projection expression directly to the complex thing instead of to whatever b is, which is what we want.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants