The transformation of equality/in-equality in a (negated) XOR is only possible
when the expressions are BIT or integer types on the SQL side (i.e. taking value
conversion into account).
Similarly, the Boolean negation `NOT` can be implemented as `~` only if the
underlying expression is a BIT.
Fixes#35093.
PopulateIncludeCollection (as well as couple other methods) take a number of delegate arguments. For scenarios with significant number of entities we see significant perf improvement when these delegates are compiled (like we used to do in EF8), rather than inlined.
Fixes#35212
* Return null when the type is nullable for Cosmos Max/Min/Average
Fixes#35094
This was a regression resulting from the major Cosmos query refactoring that happened in EF9. In EF8, the functions Min, Max, and Average would return null if the return type was nullable or was cast to a nullable when the collection is empty. In EF9, this started throwing, which is correct for non-nullable types, but a regression for nullable types.
* Added notes
In EF9 we changed the way we generate shapers in preparation for AOT scenarios. We no longer can embed arbitrary objects into the shaper, instead we need to provide a way to construct that object in code (using LiftableConstant mechanism), or simulate the functionality it used to provide.
At the end of our processing, we find all liftable constants and for the non-AOT case we compile their resolver lambdas and invoke the result with liftable context object to produce the resulting constant object we initially wanted. (in AOT case we generate code from the resolver lambda).
Problem is that we are compiling the resolver lambda in the interpretation mode - if the final product is itself a delegate, that delegate will itself be in the interpreter mode and therefore less efficient.
Fix is to use regular compilation rather than interpretation.
Fixes#35208
This is part of a fix for a larger perf issue: #35053
Don't use Expression.Invoke in ValueComparer.ObjectEqualsExpression.
ValueComparer now contains the information on how to build an expression representing Equals(object, object), which uses Expression.Invoke. We found this to be a major performance problem in some scenarios (e.g. include collection navigation) where that expression is executed large number of times by the result coordinator, as it is part of the parent/outer/selfIdentifierValueComparers.
We actually know the lambda expression that is invoked in advance, so it's much more efficient to just remap the arguments and inline the lambda body into the ObjectEqualsExpression result.
Benchmark results:
ef 8
| Method | Async | Mean | Error | StdDev | Op/s | Gen0 | Gen1 | Allocated |
|-------------------------- |------ |---------:|--------:|--------:|------:|----------:|----------:|----------:|
| PredicateMultipleIncludes | False | 147.2 ms | 2.63 ms | 2.46 ms | 6.793 | 4000.0000 | 3000.0000 | 26.24 MB |
| PredicateMultipleIncludes | True | 159.1 ms | 3.00 ms | 2.95 ms | 6.287 | 5500.0000 | 3000.0000 | 34.47 MB |
ef 9 without this change
| Method | Async | Mean | Error | StdDev | Op/s | Gen0 | Gen1 | Allocated |
|-------------------------- |------ |---------:|--------:|--------:|------:|-----------:|----------:|----------:|
| PredicateMultipleIncludes | False | 322.6 ms | 0.97 ms | 0.86 ms | 3.099 | 13000.0000 | 6000.0000 | 79.48 MB |
| PredicateMultipleIncludes | True | 344.9 ms | 6.79 ms | 6.67 ms | 2.899 | 14000.0000 | 7000.0000 | 87.72 MB |
ef 9 with this change
| Method | Async | Mean | Error | StdDev | Op/s | Gen0 | Gen1 | Allocated |
|-------------------------- |------ |---------:|--------:|--------:|------:|-----------:|----------:|----------:|
| PredicateMultipleIncludes | False | 242.8 ms | 2.39 ms | 2.12 ms | 4.119 | 8000.0000 | 5000.0000 | 51.69 MB |
| PredicateMultipleIncludes | True | 263.4 ms | 2.21 ms | 2.06 ms | 3.797 | 10000.0000 | 9000.0000 | 59.93 MB |
Benchmarks indicate that this change represents a sizable chunk of the perf regression introduced in EF9 by the AOT changes, but doesn't fully address it.
Part of #35053