|
2 | 2 | title: What's New in EF Core 7.0
|
3 | 3 | description: Overview of new features in EF Core 7.0
|
4 | 4 | author: ajcvickers
|
5 |
| -ms.date: 08/24/2022 |
| 5 | +ms.date: 08/30/2022 |
6 | 6 | uid: core/what-is-new/ef-core-7
|
7 | 7 | ---
|
8 | 8 |
|
@@ -283,6 +283,9 @@ This aggregate type contains several nested types and collections. Calls to `Own
|
283 | 283 | -->
|
284 | 284 | [!code-csharp[PostMetadataConfig](../../../../samples/core/Miscellaneous/NewInEFCore7/JsonColumnsSample.cs?name=PostMetadataConfig)]
|
285 | 285 |
|
| 286 | +> [!TIP] |
| 287 | +> `ToJson` is only needed on the aggregate root to map the entire aggregate to a JSON document. |
| 288 | +
|
286 | 289 | With this mapping, EF7 can create and query into a complex JSON document like this:
|
287 | 290 |
|
288 | 291 | ```json
|
@@ -447,6 +450,95 @@ WHERE CAST(JSON_VALUE([p].[Metadata],'$.Views') AS int) > 3000
|
447 | 450 | > [!NOTE]
|
448 | 451 | > More complex queries involving JSON collections require `jsonpath` support. Vote for [Support jsonpath querying](https://github.com/dotnet/efcore/issues/28616) if this is something you are interested in.
|
449 | 452 |
|
| 453 | +> [!TIP] |
| 454 | +> Consider creating indexes to improve query performance in JSON documents. For example, see [Index Json data](/sql/relational-databases/json/index-json-data) when using SQL Server. |
| 455 | +
|
| 456 | +### Updating JSON columns |
| 457 | + |
| 458 | +[`SaveChanges` and `SaveChangesAsync`](xref:core/saving/basic) work in the normal way to make updates a JSON column. For extensive changes, the entire document will be updated. For example, replacing most of the `Contact` document for an author: |
| 459 | + |
| 460 | +<!-- |
| 461 | + var jeremy = await context.Authors.SingleAsync(author => author.Name.StartsWith("Jeremy")); |
| 462 | +
|
| 463 | + jeremy.Contact = new() { Address = new("2 Riverside", "Trimbridge", "TB1 5ZS", "UK"), Phone = "01632 88346" }; |
| 464 | +
|
| 465 | + await context.SaveChangesAsync(); |
| 466 | +--> |
| 467 | +[!code-csharp[UpdateDocument](../../../../samples/core/Miscellaneous/NewInEFCore7/JsonColumnsSample.cs?name=UpdateDocument)] |
| 468 | + |
| 469 | +In this case, the entire new document is passed as a parameter to the `Update` command: |
| 470 | + |
| 471 | +```text |
| 472 | +info: 8/30/2022 20:21:24.392 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) |
| 473 | + Executed DbCommand (2ms) [Parameters=[@p0='{"Phone":"01632 88346","Address":{"City":"Trimbridge","Country":"UK","Postcode":"TB1 5ZS","Street":"2 Riverside"}}' (Nullable = false) (Size = 114), @p1='2'], CommandType='Text', CommandTimeout='30'] |
| 474 | +``` |
| 475 | + |
| 476 | +```sql |
| 477 | + SET IMPLICIT_TRANSACTIONS OFF; |
| 478 | + SET NOCOUNT ON; |
| 479 | + UPDATE [Authors] SET [Contact] = @p0 |
| 480 | + OUTPUT 1 |
| 481 | + WHERE [Id] = @p1; |
| 482 | +``` |
| 483 | + |
| 484 | +However, if only a sub-document is changed, then EF Core will use a "JSON_MODIFY" command to update only the sub-document. For example, changing the `Address` inside a `Contact` document: |
| 485 | + |
| 486 | +<!-- |
| 487 | + var brice = await context.Authors.SingleAsync(author => author.Name.StartsWith("Brice")); |
| 488 | +
|
| 489 | + brice.Contact.Address = new("4 Riverside", "Trimbridge", "TB1 5ZS", "UK"); |
| 490 | +
|
| 491 | + await context.SaveChangesAsync(); |
| 492 | +--> |
| 493 | +[!code-csharp[UpdateSubDocument](../../../../samples/core/Miscellaneous/NewInEFCore7/JsonColumnsSample.cs?name=UpdateSubDocument)] |
| 494 | + |
| 495 | +Generates the following SQL: |
| 496 | + |
| 497 | +```text |
| 498 | +info: 8/30/2022 20:53:01.669 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) |
| 499 | + Executed DbCommand (2ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] |
| 500 | + SELECT TOP(2) [a].[Id], [a].[Name], JSON_QUERY([a].[Contact],'$') |
| 501 | + FROM [Authors] AS [a] |
| 502 | + WHERE [a].[Name] LIKE N'Brice%' |
| 503 | +``` |
| 504 | + |
| 505 | +```sql |
| 506 | +info: 8/30/2022 20:53:01.676 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) |
| 507 | + Executed DbCommand (2ms) [Parameters=[@p0='{"City":"Trimbridge","Country":"UK","Postcode":"TB1 5ZS","Street":"4 Riverside"}' (Nullable = false) (Size = 80), @p1='5'], CommandType='Text', CommandTimeout='30'] |
| 508 | + SET IMPLICIT_TRANSACTIONS OFF; |
| 509 | + SET NOCOUNT ON; |
| 510 | + UPDATE [Authors] SET [Contact] = JSON_MODIFY([Contact], 'strict $.Address', JSON_QUERY(@p0)) |
| 511 | + OUTPUT 1 |
| 512 | + WHERE [Id] = @p1; |
| 513 | +``` |
| 514 | + |
| 515 | +Finally, if only a single property is changed, then EF Core will again use a "JSON_MODIFY" command, this time to patch only the changed property value. For example: |
| 516 | + |
| 517 | +<!-- |
| 518 | + var arthur = await context.Authors.SingleAsync(author => author.Name.StartsWith("Arthur")); |
| 519 | +
|
| 520 | + arthur.Contact.Address.Country = "United Kingdom"; |
| 521 | +
|
| 522 | + await context.SaveChangesAsync(); |
| 523 | +--> |
| 524 | +[!code-csharp[UpdateProperty](../../../../samples/core/Miscellaneous/NewInEFCore7/JsonColumnsSample.cs?name=UpdateProperty)] |
| 525 | + |
| 526 | +Generates the following SQL: |
| 527 | + |
| 528 | +```text |
| 529 | +info: 8/30/2022 20:24:04.677 RelationalEventId.CommandExecuted[20101] (Microsoft.EntityFrameworkCore.Database.Command) |
| 530 | + Executed DbCommand (2ms) [Parameters=[@p0='["United Kingdom"]' (Nullable = false) (Size = 18), @p1='4'], CommandType='Text', CommandTimeout='30'] |
| 531 | +``` |
| 532 | + |
| 533 | +```sql |
| 534 | +SET IMPLICIT_TRANSACTIONS OFF; |
| 535 | +SET NOCOUNT ON; |
| 536 | +UPDATE [Authors] SET [Contact] = JSON_MODIFY( |
| 537 | + [Contact], 'strict $.Address.Country', JSON_VALUE(@p0, '$[0]')) |
| 538 | +OUTPUT 1 |
| 539 | +WHERE [Id] = @p1; |
| 540 | +``` |
| 541 | + |
450 | 542 | ## ExecuteUpdate and ExecuteDelete (Bulk updates)
|
451 | 543 |
|
452 | 544 | By default, EF Core [tracks changes to entities](xref:core/change-tracking/index), and then [sends updates to the database](xref:core/saving/index) when one of the `SaveChanges` methods is called. Changes are only sent for properties and relationships that have actually changed. Also, the tracked entities remain in sync with the changes sent to the database. This mechanism is an efficient and convenient way to send general-purpose inserts, updates, and deletes to the database. These changes are also batched to reduce the number of database round-trips.
|
@@ -640,21 +732,28 @@ The statement has been terminated.
|
640 | 732 | To fix this, we must first either delete the posts, or sever the relationship between each post and its author by setting `AuthorId` foreign key property to null. For example, using the delete option:
|
641 | 733 |
|
642 | 734 | <!--
|
643 |
| - await context.Posts.ExecuteDeleteAsync(); |
644 |
| - await context.Authors.ExecuteDeleteAsync(); |
| 735 | + await context.Posts.TagWith("Deleting posts...").ExecuteDeleteAsync(); |
| 736 | + await context.Authors.TagWith("Deleting authors...").ExecuteDeleteAsync(); |
645 | 737 | -->
|
646 | 738 | [!code-csharp[DeleteAllAuthors](../../../../samples/core/Miscellaneous/NewInEFCore7/ExecuteDeleteSample.cs?name=DeleteAllAuthors)]
|
647 | 739 |
|
| 740 | +> [!TIP] |
| 741 | +> `TagWith` can be used to tag `ExecuteDelete` or `ExecuteUpdate` in the same way as it tags normal queries. |
| 742 | +
|
648 | 743 | This results in two separate commands; the first to delete the dependents:
|
649 | 744 |
|
650 | 745 | ```sql
|
| 746 | +-- Deleting posts... |
| 747 | + |
651 | 748 | DELETE FROM [p]
|
652 | 749 | FROM [Posts] AS [p]
|
653 | 750 | ```
|
654 | 751 |
|
655 | 752 | And the second to delete the principals:
|
656 | 753 |
|
657 | 754 | ```sql
|
| 755 | +-- Deleting authors... |
| 756 | + |
658 | 757 | DELETE FROM [a]
|
659 | 758 | FROM [Authors] AS [a]
|
660 | 759 | ```
|
|
0 commit comments