You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Previously EF returned inconsistent results for the `ToString()` method when the argument value was `null`. E.g. `ToString()` on `bool?` property with `null` value returned `null`, but for non-property `bool?` expressions whose value was `null` it returned `True`. The behavior was also incosistent for other data types, e.g. `ToString()` on `null` value enum returned empty string.
93
+
94
+
#### New behavior
95
+
96
+
Starting with EF Core 9.0, the `ToString()` method now consistently returns empty string in all cases when the argument value is `null`.
97
+
98
+
#### Why
99
+
100
+
The old behavior was inconsistent across different data types and situations, as well as not aligned with the [C# behavior](/dotnet/api/system.nullable-1.tostring#returns).
101
+
102
+
#### Mitigations
103
+
104
+
To revert to the old behavior, rewrite the query accordingly:
Extensive work has gone into making the Azure Cosmos DB provider better in 9.0. The changes include a number of high-impact breaking changes; if you are upgrading an existing application, please read the following carefully.
Copy file name to clipboardExpand all lines: entity-framework/core/what-is-new/ef-core-9.0/whatsnew.md
+188-4Lines changed: 188 additions & 4 deletions
Original file line number
Diff line number
Diff line change
@@ -615,6 +615,8 @@ FROM [Posts] AS [p]
615
615
WHERE [p].[Title] = N'.NET Blog' AND [p].[Id] = 1
616
616
```
617
617
618
+
#### The `EF.Parameter` method
619
+
618
620
EF9 introduces the `EF.Parameter` method to do the opposite. That is, force EF to use a parameter even if the value is a constant in code. For example:
619
621
620
622
<!--
@@ -635,6 +637,59 @@ FROM [Posts] AS [p]
635
637
WHERE [p].[Title] = @__p_0 AND [p].[Id] = @__id_1
636
638
```
637
639
640
+
<aname="parameterized-collections"></a>
641
+
642
+
#### Parameterized primitive collections
643
+
644
+
EF8 changed the way [some queries that use primitive collections are translated](xref:core/what-is-new/ef-core-8.0/whatsnew#queries-with-primitive-collections). When a LINQ query contains a parameterized primitive collection, EF converts its contents to JSON and pass it as a single parameter value the query:
WHERE [p].[Title] = N'.NET Blog' AND [p].[Id] IN (
662
+
SELECT [i].[value]
663
+
FROM OPENJSON(@__ids_0) WITH ([value] int '$') AS [i]
664
+
)
665
+
```
666
+
667
+
This allows having the same SQL query for different parameterized collections (only the parameter value changes), but in some situations it can lead to performance issues as the database isn't able to optimally plan for the query. The `EF.Constant` method can be used to revert to the previous translation.
668
+
669
+
The following query uses `EF.Constant` to that effect:
WHERE [p].[Title] = N'.NET Blog'AND [p].[Id] IN (1, 2, 3)
686
+
```
687
+
688
+
Moreover, EF9 introduces `TranslateParameterizedCollectionsToConstants`[context option](/ef/core/dbcontext-configuration/#dbcontextoptions) that can be used to prevent primitive collection parameterization for all queries. We also added a complementing `TranslateParameterizedCollectionsToParameters` which forces parameterization of primitive collections explicitly (this is the default behavior).
689
+
690
+
> [!TIP]
691
+
> The `EF.Parameter` method overrides the context option. If you want to prevent parameterization of primitive collections for most of your queries (but not all), you can set the context option `TranslateParameterizedCollectionsToConstants` and use `EF.Parameter` for the queries or individual variables that you want to parameterize.
692
+
638
693
<aname="inlinedsubs"></a>
639
694
640
695
### Inlined uncorrelated subqueries
@@ -685,7 +740,71 @@ ORDER BY (SELECT 1)
685
740
OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY
686
741
```
687
742
688
-
<aname="hashsetasync"></a>
743
+
<aname="aggregate-over-subquery"></a>
744
+
745
+
### Aggregate functions over subqueries and aggregates on SQL Server
746
+
747
+
EF9 improves the translation of some complex queries using aggregate functions composed over subqueries or other aggregate functions.
748
+
Below is an example of such query:
749
+
750
+
<!--
751
+
var latestPostsAverageRatingByLanguage = await context.Blogs.
First, `Select` computes `LatestPostRating` for each `Post` which requires a subquery when translating to SQL. Later in the query these results are aggregated using `Average` operation. The resulting SQL looks as follows when run on SQL Server:
764
+
765
+
```sql
766
+
SELECTAVG([s].[Rating])
767
+
FROM [Blogs] AS [b]
768
+
OUTER APPLY (
769
+
SELECT TOP(1) [p].[Rating]
770
+
FROM [Posts] AS [p]
771
+
WHERE [b].[Id] = [p].[BlogId]
772
+
ORDER BY [p].[PublishedOn] DESC
773
+
) AS [s]
774
+
GROUP BY [b].[Language]
775
+
```
776
+
777
+
In previous versions EF Core would generate invalid SQL for similar queries, trying to apply the aggregate operation directly over the subquery. This is not allowed on SQL Server and results in an exception.
778
+
Same principle applies to queries using aggregate over another aggregate:
779
+
780
+
<!--
781
+
var topRatedPostsAverageRatingByLanguage = await context.Blogs.
> This change doesn't affect Sqlite, which supports aggregates over subqueries (or other aggregates) and it does not support `LATERAL JOIN` (`APPLY`). Below is the SQL for the first query running on Sqlite:
795
+
>
796
+
> ```sql
797
+
>SELECT ef_avg((
798
+
>SELECT"p"."Rating"
799
+
>FROM"Posts"AS"p"
800
+
>WHERE"b"."Id"="p"."BlogId"
801
+
>ORDER BY"p"."PublishedOn"DESC
802
+
>LIMIT1))
803
+
>FROM"Blogs"AS"b"
804
+
>GROUP BY"b"."Language"
805
+
>```
806
+
807
+
<a name="count-not-zero"></a>
689
808
690
809
### Queries using Count != 0 are optimized
691
810
@@ -712,6 +831,8 @@ WHERE EXISTS (
712
831
WHERE"b"."Id"="p"."BlogId")
713
832
```
714
833
834
+
<aname="comparison-null-semantics"></a>
835
+
715
836
### C# semantics for comparison operations on nullable values
716
837
717
838
In EF8 comparisons between nullable elements were not performed correctly for some scenarios. In C#, if one or both operands are null, the result of a comparison operation is false; otherwise, the contained values of operands are compared. In EF8 we used to translate comparisons using database null semantics. This would produce results different than similar query using LINQ to Objects.
@@ -782,6 +903,59 @@ EF9 now properly handles these scenarios, producing results consistent with LINQ
782
903
783
904
This enhancement was contributed by [@ranma42](https://github.com/ranma42). Many thanks!
784
905
906
+
<aname="order-operator"></a>
907
+
908
+
### Translation of `Order` and `OrderDescending` LINQ operators
909
+
910
+
EF9 enables the translation of LINQ simplified ordering operations (`Order` and `OrderDescending`). These work similar to `OrderBy`/`OrderByDescending` but don't require an argument. Instead, they apply default ordering - for entities this means ordering based on primary key values and for other types, ordering based on the values themselves.
911
+
912
+
Below is an example query which takes advantage of the simplified ordering operators:
LEFT JOIN [Posts] AS [p] ON [b].[Id] = [p].[BlogId]
948
+
LEFT JOIN [Posts] AS [p0] ON [b].[Id] = [p0].[BlogId]
949
+
ORDER BY [b].[Id], [p].[Id] DESC, [p0].[Title]
950
+
```
951
+
952
+
> [!NOTE]
953
+
> `Order` and `OrderDescending` methods are only supported for collections of entities, complex types or scalars - they will not work on more complex projections, e.g. collections of anonymous types containing multiple properties.
954
+
955
+
This enhancement was contributed by the EF Team alumnus [@bricelam](https://github.com/bricelam). Many thanks!
956
+
957
+
<aname="improved-negation"></a>
958
+
785
959
### Improved translation of logical negation operator (!)
786
960
787
961
EF9 brings many optimizimations around SQL `CASE/WHEN`, `COALESCE`, negation, and various other constructs; most of these were contributed by Andrea Canciani ([@ranma42](https://github.com/ranma42)) - many thanks for all of these! Below, we'll detail just a few of these optimizations around logical negation.
@@ -848,7 +1022,7 @@ On SQL Server, when projecting a negated bool property:
848
1022
<!--
849
1023
var negatedBoolProjection = await context.Posts.Select(x => new { x.Title, Active = !x.Archived }).ToListAsync();
### Better support for Azure SQL and Azure Synapse
1047
+
1048
+
EF9 allows for more flexibility when specifying the type of SQL Server which is being targeted. Instead of configuring EF with `UseSqlServer`, you can now specify `UseAzureSql` or `UseAzureSynapse`.
1049
+
This allows EF to produce better SQL when using Azure SQL or Azure Synapse. EF can take advantage of the database specific features (e.g. [dedicated type for JSON on Azure SQL](/sql/t-sql/data-types/json-data-type)), or work around its limitations (e.g. [`ESCAPE` clause is not available when using `LIKE` on Azure Synapse](/sql/t-sql/language-elements/like-transact-sql#syntax)).
1050
+
870
1051
### Other query improvements
871
1052
872
1053
* The primitive collections querying support [introduced in EF8](xref:core/what-is-new/ef-core-8.0/whatsnew#queries-with-primitive-collections) has been extended to support all `ICollection<T>` types. Note that this applies only to parameter and inline collections - primitive collections that are part of entities are still limited to arrays, lists and [in EF9 also read-only arrays/lists](#read-only-primitive-collections).
@@ -878,6 +1059,9 @@ FROM [Posts] AS [p]
878
1059
*`Sum` and `Average` now work for decimals on SQLite ([#33721](https://github.com/dotnet/efcore/pull/33721), contributed by [@ranma42](https://github.com/ranma42)).
879
1060
* Fixes and optimizations to `string.StartsWith` and `EndsWith` ([#31482](https://github.com/dotnet/efcore/pull/31482)).
880
1061
*`Convert.To*` methods can now accept argument of type `object` ([#33891](https://github.com/dotnet/efcore/pull/33891), contributed by [@imangd](https://github.com/imangd)).
1062
+
* Exclusive-Or (XOR) operation is now translated on SQL Server ([#34071](https://github.com/dotnet/efcore/pull/34071), contributed by [@ranma42](https://github.com/ranma42)).
1063
+
* Optimizations around nullability for `COLLATE` and `AT TIME ZONE` operations ([#34263](https://github.com/dotnet/efcore/pull/34263), contributed by [@ranma42](https://github.com/ranma42)).
1064
+
* Optimizations for `DISTINCT` over `IN`, `EXISTS` and set operations ([#34381](https://github.com/dotnet/efcore/pull/34381), contributed by [@ranma42](https://github.com/ranma42)).
881
1065
882
1066
The above were only some of the more important query improvements in EF9; see [this issue](https://github.com/dotnet/efcore/issues/34151) for a more complete listing.
0 commit comments