-
Notifications
You must be signed in to change notification settings - Fork 3.2k
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
Fix to #35024 - Query could not be translated when using a static ICollection/IList field #35029
base: main
Are you sure you want to change the base?
Conversation
…llection/IList field Problem was that when converting primitive collection to inline query root we were not matching the expression if it was constant wrapped in convert. Fix is to remove convert for the purpose of pattern match for the transformation. Fixes #35024
@maumar That looks fine. I did though ask myself where the Turns out it gets added as part of the funcletizer
LH Type is If you want to prevent the convert you could go something like this
|
@ChrisJollyAU I'm bit apprehensive about adding (more) reflection to the funcletizer as it is a perf-sensitive component. We'd also need to cover the case of DeriveType : BaseType (e.g. ObservableCollection), so that's some more reflection. Although it would fix the problem at the very source. @roji any thoughts on this? |
As a side note just FYI. The |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a correct fix for the specific issue, but I think the general behavior of constantizing "init-only" (code) is problematic... The fact that a (static) member is init-only (readonly) doesn't mean that it's immutable; for example, in the example of the test you just wrote, there's nothing preventing the contents StaticIds from being mutated (even if StaticIds itself is readonly). But the way we currently translate, the contents of the list are captured and cached the first time the query is compiled (because they're constantized).
In other words, I thing we have an unrelated bug here; if that bug is fixed (by no longer constantizing init-only fields), then we'd no longer have the Convert node that is also causing the trouble here.
(note that if users really do want to constantize, they can still do that by introducing EF.Constant()).
What do you think?
|
||
[ConditionalTheory] | ||
[MemberData(nameof(IsAsyncData))] | ||
public virtual Task Contains_with_static_IList(bool async) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a specific reason this is in this test class (there doesn't seem to be anything aggregate about the query).
Maybe PrimitiveCollectionsQueryTestBase is a better place...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there is a lot of Contains tests in that class, so just keeping them all together. I don't mind moving them all to PCQTB as I do agree it's better place for them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not very important obviously... Whatever you prefer
Aside from my above comment, @ChrisJollyAU's suggestion makes sense to me - I think simply checking |
I did experiment with changing that to be Indexof_with_emptystring
new sql
Note that we seem to lose the optimization for that depends on a literal empty string constant. Contains_on_readonly_enumerable
New sql
Array_cast_to_IEnumerable_Contains_with_constant
New sql
Specially in the above cases it probably makes more sense to constantize. It also breaks |
@ChrisJollyAU thanks for taking a look; it definitely makes sense that some optimizations would be lost due to moving from constantization to parameterization. The problem here is that we can't reliably know when the type in question is fully immutable, which is when it's safe to constantize. We could have a hard-coded list of the common immutable types (string, int, DateTime...) and constantize for those (when they're init-only of course), but constantizing for a type list |
Off the top of my head, I think the only ones with the problem are those like List, Collection, Queue, HashSet, Dictionary i.e. those that implement IList and ICollection. But definitely for your basic types that you know are completely immutable (like a readonly string or int) my thoughts are towards keeping the constantizing
Is that because of the caching or something like that (because its not in the actual sql thats generated as that works) |
Yeah, agree.
Apologies - I thought a little bit about this, and I was wrong above. The problem with constantization of a mutable thing (e.g. So I don't think it's quite a bug, more like a problematic performance problem. |
Problem was that when converting primitive collection to inline query root we were not matching the expression if it was constant wrapped in convert. Fix is to remove convert for the purpose of pattern match for the transformation.
Fixes #35024