update

2881099
2022-05-04 23:53:42 +08:00
parent 4476f30f36
commit cfb455bc6e
3 changed files with 145 additions and 121 deletions

@@ -1,128 +1,147 @@
[中文](%e8%81%94%e7%ba%a7%e4%bf%9d%e5%ad%98) | **English** [中文](%e8%81%94%e7%ba%a7%e4%bf%9d%e5%ad%98) | **English**
The cascade save function can save the one-to-many or many-to-many navigation properties when saving the object. This document introduces cascading saving to avoid misuse by developers. ## SaveMany Savemany save manually
## One-to-Many Cascade Saving Save completely, compare the existing data in the table, and calculate the execution of addition, modification and deletion.
Method 1: Save intact, compare the existing data in the table, and calculate the added, modified, and deleted data
```csharp ```csharp
var repo = fsql.GetRepository<T>(); class Cagetory
repo.Insert(item); {
repo.SaveMany(item, "Childs");
```
- It is possible to delete the existing data in the table, confirm?
- When the Childs property is Empty, delete all the table data of Childs in the item, confirm?
- When saving Topics, the subordinate collection properties of Childs\[0-..\] are not saved, only the current level of Topics. Confirm?
Method 2: Additional save, do not delete the existing data in the table
```csharp
var repo = fsql.GetRepository<T>();
repo.DbContextOptions.EnableCascadeSave = true; //Need to be opened manually
repo.Insert(item);
```
- Do not delete the existing data in the table, confirm?
- When the Childs property is Empty, do nothing, confirm?
- When saving Childs, the subordinate collection properties of Childs\[0-..\] will also be saved, _down 18 levels_, confirm?
> _Down 18 levels_ is, the navigation properties that exist in the navigation properties, in which there are navigation properties.
> The navigation properties of the navigation properties are retrieved layer by layer, and the InsertOrUpdate operation is performed together.
## Many-to-Many Cascade Saving
There is only one mechanism: complete preservation.
> Enabling the `EnableCascadeSave` option or `SaveMany` is a complete save.
---
Test 1: Additional Save OneToMany
```csharp
[Table(Name = "EAUNL_OTMP_CT")]
class CagetoryParent {
public Guid Id { get; set; } public Guid Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public Guid ParentId { get; set; } public Guid ParentId { get; set; }
[Navigate(nameof(ParentId))] [Navigate(nameof(ParentId))]
public List<CagetoryParent> Childs { get; set; } public List<Cagetory> Childs { get; set; }
} }
//item = ...;
var repo = fsql.GetRepository<Cagetory>();
repo.Insert(item);
repo.SaveMany(item, "Childs");
```
-[OneToMany] and [ManyToMany] navigation attributes are supported
-Save only children, not recursive tracing down
-When children is empty, delete all children table data existing in item, and confirm?
-Advantages: simple mechanism, good control and safety
-Disadvantages: not intelligent
## EnableCascadeSave DbContext/Repository
DbContext/Repository EnableCascadeSave enables recursive tracing of the OneToOne, OneToMany, and ManyToMany navigation properties of objects when they are saved. This document describes the mechanism to prevent misuse.
1. OneToOne cascade save
> v3.2.606 + support, and support [cascade deletion function](https://github.com/dotnetcore/FreeSql/wiki/%e5%88%a0%e9%99%a4#ibaserepository-%E7%BA%A7%E8%81%94%E5%88%A0%E9%99%A4)
2. OneToMany appends or updates the sub table without deleting the existing data of the sub table
```c#
var repo = fsql. GetRepository<Cagetory>();
repo. DbContextOptions. EnableCascadeSave = true; // Manual opening required
repo. Insert(item);
```
-Do not delete the existing data of children sub table. Are you sure?
-When the children attribute is empty, do not do anything. Confirm?
-When you save children, you will also save the child set attributes of children \ [0 -.. \]. Go down to 18 layers and confirm?
>For example, in the [type] table, there is set attribute [article] below and set attribute [comment] below [article].
>When saving the [type] table object, it will retrieve the set attribute [article], and then if the [article] is saved, it will continue to retrieve the set attribute [comment]. Do insertorupdate operation together.
3. ManyToMany completely compares and saves the intermediate table and appends the external table
Compare and save the intermediate table completely, compare the existing data of the [many to many] intermediate table, and calculate the execution of addition, modification and deletion.
Append external tables, only append without updating.
-This table song
-External table tag
-Intermediate table Songtag
---
Test 1: append and save OneToMany
```csharp
[Fact] [Fact]
public void TestOneToManyParent() { public void TestOneToManyParent()
var repo = fsql.GetRepository<CagetoryParent>(); {
var repo = fsql.GetRepository<Cagetory>();
repo.DbContextOptions.EnableCascadeSave = true; repo.DbContextOptions.EnableCascadeSave = true;
var cts = new[] { var cts = new[]
new CagetoryParent {
new Cagetory
{ {
Name = "Category1", Name = "class1",
Childs = new List<CagetoryParent>(new[] Childs = new List<Cagetory>(new[]
{ {
new CagetoryParent { Name = "Category1_1" }, new Cagetory { Name = "class1_1" },
new CagetoryParent { Name = "Category1_2" }, new Cagetory { Name = "class1_2" },
new CagetoryParent { Name = "Category1_3" } new Cagetory { Name = "class1_3" }
}) })
}, },
new CagetoryParent new Cagetory
{ {
Name = "Category2", Name = "class2",
Childs = new List<CagetoryParent>(new[] Childs = new List<Cagetory>(new[]
{ {
new CagetoryParent { Name = "Category2_1" }, new Cagetory { Name = "class2_1" },
new CagetoryParent { Name = "Category2_2" } new Cagetory { Name = "class2_2" }
}) })
} }
}; };
repo.Insert(cts); repo.Insert(cts);
//Perform table creation and insert data: //To create a table and insert data:
//INSERT INTO "EAUNL_OTMP_CT"("Id", "Name", "ParentId") VALUES('5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f', 'Category1', '00000000-0000-0000-0000-000000000000'), ('5d90afcb-ed57-f6f4-0082-cb6c5b531b3e', 'Category2', '00000000-0000-0000-0000-000000000000') //INSERT INTO "Cagetory"("Id", "Name", "ParentId") VALUES('5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f', 'class1', '00000000-0000-0000-0000-000000000000'), ('5d90afcb-ed57-f6f4-0082-cb6c5b531b3e', 'class2', '00000000-0000-0000-0000-000000000000')
//INSERT INTO "EAUNL_OTMP_CT"("Id", "Name", "ParentId") VALUES('5d90afcb-ed57-f6f4-0082-cb6d0c1c5f1a', 'Category1_1', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afcb-ed57-f6f4-0082-cb6e74bd8eef', 'Category1_2', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afcb-ed57-f6f4-0082-cb6f6267cc5f', 'Category1_3', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afcb-ed57-f6f4-0082-cb7057c41d46', 'Category2_1', '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e'), ('5d90afcb-ed57-f6f4-0082-cb7156e0375e', 'Category2_2', '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e') //INSERT INTO "Cagetory"("Id", "Name", "ParentId") VALUES('5d90afcb-ed57-f6f4-0082-cb6d0c1c5f1a', 'class1_1', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afcb-ed57-f6f4-0082-cb6e74bd8eef', 'class1_2', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afcb-ed57-f6f4-0082-cb6f6267cc5f', 'class1_3', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afcb-ed57-f6f4-0082-cb7057c41d46', 'class2_1', '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e'), ('5d90afcb-ed57-f6f4-0082-cb7156e0375e', 'class2_2', '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e')
cts[0].Name = "Category11"; cts[0].Name = "class11";
cts[0].Childs.Clear(); cts[0].Childs.Clear();
cts[1].Name = "Category22"; cts[1].Name = "class22";
cts[1].Childs.Clear(); cts[1].Childs.Clear();
repo.Update(cts); repo.Update(cts);
//UPDATE "EAUNL_OTMP_CT" SET "Name" = CASE "Id" //UPDATE "Cagetory" SET "Name" = CASE "Id"
//WHEN '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f' THEN 'Category11' //WHEN '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f' THEN 'class11'
//WHEN '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e' THEN 'Category22' END //WHEN '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e' THEN 'class22' END
//WHERE ("Id" IN ('5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f','5d90afcb-ed57-f6f4-0082-cb6c5b531b3e')) //WHERE ("Id" IN ('5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f','5d90afcb-ed57-f6f4-0082-cb6c5b531b3e'))
//After Childs.Clear, the delete sub-collection operation is not executed, indicating that a complete comparison has not been made //Childs.Clear After that, the operation of deleting subsets was not performed, indicating that no complete comparison was made
cts[0].Name = "Category111"; cts[0].Name = "class111";
cts[0].Childs.Clear(); cts[0].Childs.Clear();
cts[0].Childs.Add(new CagetoryParent { Name = "Category1_33" }); cts[0].Childs.Add(new Cagetory { Name = "class1_33" });
cts[1].Name = "Category222"; cts[1].Name = "class222";
cts[1].Childs.Clear(); cts[1].Childs.Clear();
cts[1].Childs.Add(new CagetoryParent { Name = "Category2_22" }); cts[1].Childs.Add(new Cagetory { Name = "class2_22" });
repo.Update(cts); repo.Update(cts);
//UPDATE "EAUNL_OTMP_CT" SET "Name" = CASE "Id" //UPDATE "Cagetory" SET "Name" = CASE "Id"
//WHEN '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f' THEN 'Category111' //WHEN '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f' THEN 'class111'
//WHEN '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e' THEN 'Category222' END //WHEN '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e' THEN 'class222' END
//WHERE ("Id" IN ('5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f','5d90afcb-ed57-f6f4-0082-cb6c5b531b3e')) //WHERE ("Id" IN ('5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f','5d90afcb-ed57-f6f4-0082-cb6c5b531b3e'))
//INSERT INTO "EAUNL_OTMP_CT"("Id", "Name", "ParentId") VALUES('5d90afe8-ed57-f6f4-0082-cb725df546ea', 'Category1_33', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afe8-ed57-f6f4-0082-cb7338a6214c', 'Category2_22', '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e') //INSERT INTO "Cagetory"("Id", "Name", "ParentId") VALUES('5d90afe8-ed57-f6f4-0082-cb725df546ea', 'class1_33', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afe8-ed57-f6f4-0082-cb7338a6214c', 'class2_22', '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e')
} }
``` ```
Test 2: Additional Save ManyToMany ---
Test 2: Full save ManyToMany
```csharp ```csharp
[Table(Name = "EAUNL_MTM_SONG")] class Song
class Song { {
public Guid Id { get; set; } public Guid Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
public List<Tag> Tags { get; set; } public List<Tag> Tags { get; set; }
} }
[Table(Name = "EAUNL_MTM_TAG")] class Tag
class Tag { {
public Guid Id { get; set; } public Guid Id { get; set; }
public string TagName { get; set; } public string TagName { get; set; }
public List<Song> Songs { get; set; } public List<Song> Songs { get; set; }
} }
[Table(Name = "EAUNL_MTM_SONGTAG")] class SongTag
class SongTag { {
public Guid SongId { get; set; } public Guid SongId { get; set; }
public Song Song { get; set; } public Song Song { get; set; }
public Guid TagId { get; set; } public Guid TagId { get; set; }
@@ -130,18 +149,20 @@ class SongTag {
} }
[Fact] [Fact]
public void TestManyToMany() { public void TestManyToMany()
var tags = new[] { {
new Tag { TagName = "Pop" }, var tags = new[]
new Tag { TagName = "Modern music" }, {
new Tag { TagName = "Next-generation music" }, new Tag { TagName = "pop music" },
new Tag { TagName = "Rock" } new Tag { TagName = "the post-80s generation" },
new Tag { TagName = "the post-00s generation" },
new Tag { TagName = "Rock music" }
}; };
var ss = new[] var ss = new[]
{ {
new Song new Song
{ {
Name = "Take Me To Your Heart.mp3", Name = "love you forever.mp3",
Tags = new List<Tag>(new[] Tags = new List<Tag>(new[]
{ {
tags[0], tags[1] tags[0], tags[1]
@@ -149,7 +170,7 @@ public void TestManyToMany() {
}, },
new Song new Song
{ {
Name = "Kiss Me More.mp3", Name = "Li Bai.mp3",
Tags = new List<Tag>(new[] Tags = new List<Tag>(new[]
{ {
tags[0], tags[2] tags[0], tags[2]
@@ -159,49 +180,49 @@ public void TestManyToMany() {
var repo = fsql.GetRepository<Song>(); var repo = fsql.GetRepository<Song>();
repo.DbContextOptions.EnableCascadeSave = true; repo.DbContextOptions.EnableCascadeSave = true;
repo.Insert(ss); repo.Insert(ss);
//INSERT INTO "EAUNL_MTM_SONG"("Id", "Name") VALUES('5d90fdb3-6a6b-2c58-00c8-37974177440d', 'Take Me To Your Heart.mp3'), ('5d90fdb3-6a6b-2c58-00c8-37987f29b197', 'Kiss Me More.mp3') //INSERT INTO "Song"("Id", "Name") VALUES('5d90fdb3-6a6b-2c58-00c8-37974177440d', 'love you forever.mp3'), ('5d90fdb3-6a6b-2c58-00c8-37987f29b197', 'Li Bai.mp3')
//INSERT INTO "EAUNL_MTM_TAG"("Id", "TagName") VALUES('5d90fdb7-6a6b-2c58-00c8-37991ead4f05', 'Pop'), ('5d90fdbd-6a6b-2c58-00c8-379a0432a09c', 'Modern music') //INSERT INTO "Tag"("Id", "TagName") VALUES('5d90fdb7-6a6b-2c58-00c8-37991ead4f05', 'pop music'), ('5d90fdbd-6a6b-2c58-00c8-379a0432a09c', 'the post-80s generation')
//INSERT INTO "EAUNL_MTM_SONGTAG"("SongId", "TagId") VALUES('5d90fdb3-6a6b-2c58-00c8-37974177440d', '5d90fdb7-6a6b-2c58-00c8-37991ead4f05'), ('5d90fdb3-6a6b-2c58-00c8-37974177440d', '5d90fdbd-6a6b-2c58-00c8-379a0432a09c') //INSERT INTO "SongTag"("SongId", "TagId") VALUES('5d90fdb3-6a6b-2c58-00c8-37974177440d', '5d90fdb7-6a6b-2c58-00c8-37991ead4f05'), ('5d90fdb3-6a6b-2c58-00c8-37974177440d', '5d90fdbd-6a6b-2c58-00c8-379a0432a09c')
//INSERT INTO "EAUNL_MTM_TAG"("Id", "TagName") VALUES('5d90fdcc-6a6b-2c58-00c8-379b5af59d25', 'Next-generation music') //INSERT INTO "Tag"("Id", "TagName") VALUES('5d90fdcc-6a6b-2c58-00c8-379b5af59d25', 'the post-00s generation')
//INSERT INTO "EAUNL_MTM_SONGTAG"("SongId", "TagId") VALUES('5d90fdb3-6a6b-2c58-00c8-37987f29b197', '5d90fdb7-6a6b-2c58-00c8-37991ead4f05'), ('5d90fdb3-6a6b-2c58-00c8-37987f29b197', '5d90fdcc-6a6b-2c58-00c8-379b5af59d25') //INSERT INTO "SongTag"("SongId", "TagId") VALUES('5d90fdb3-6a6b-2c58-00c8-37987f29b197', '5d90fdb7-6a6b-2c58-00c8-37991ead4f05'), ('5d90fdb3-6a6b-2c58-00c8-37987f29b197', '5d90fdcc-6a6b-2c58-00c8-379b5af59d25')
ss[0].Name = "Take Me To Your Heart.mp5"; ss[0].Name = "love you forever.mp5";
ss[0].Tags.Clear(); ss[0].Tags.Clear();
ss[0].Tags.Add(tags[0]); ss[0].Tags.Add(tags[0]);
ss[1].Name = "Kiss Me More.mp5"; ss[1].Name = "Li Bai.mp5";
ss[1].Tags.Clear(); ss[1].Tags.Clear();
ss[1].Tags.Add(tags[3]); ss[1].Tags.Add(tags[3]);
repo.Update(ss); repo.Update(ss);
//UPDATE "EAUNL_MTM_SONG" SET "Name" = CASE "Id" //UPDATE "Song" SET "Name" = CASE "Id"
//WHEN '5d90fdb3-6a6b-2c58-00c8-37974177440d' THEN 'Take Me To Your Heart.mp5' //WHEN '5d90fdb3-6a6b-2c58-00c8-37974177440d' THEN 'love you forever.mp5'
//WHEN '5d90fdb3-6a6b-2c58-00c8-37987f29b197' THEN 'Kiss Me More.mp5' END //WHEN '5d90fdb3-6a6b-2c58-00c8-37987f29b197' THEN 'Li Bai.mp5' END
//WHERE ("Id" IN ('5d90fdb3-6a6b-2c58-00c8-37974177440d','5d90fdb3-6a6b-2c58-00c8-37987f29b197')) //WHERE ("Id" IN ('5d90fdb3-6a6b-2c58-00c8-37974177440d','5d90fdb3-6a6b-2c58-00c8-37987f29b197'))
//SELECT a."SongId", a."TagId" //SELECT a."SongId", a."TagId"
//FROM "EAUNL_MTM_SONGTAG" a //FROM "SongTag" a
//WHERE (a."SongId" = '5d90fdb3-6a6b-2c58-00c8-37974177440d') //WHERE (a."SongId" = '5d90fdb3-6a6b-2c58-00c8-37974177440d')
//DELETE FROM "EAUNL_MTM_SONGTAG" WHERE ("SongId" = '5d90fdb3-6a6b-2c58-00c8-37974177440d' AND "TagId" = '5d90fdbd-6a6b-2c58-00c8-379a0432a09c') //DELETE FROM "SongTag" WHERE ("SongId" = '5d90fdb3-6a6b-2c58-00c8-37974177440d' AND "TagId" = '5d90fdbd-6a6b-2c58-00c8-379a0432a09c')
//INSERT INTO "EAUNL_MTM_TAG"("Id", "TagName") VALUES('5d90febd-6a6b-2c58-00c8-379c21acfc72', 'Rock') //INSERT INTO "Tag"("Id", "TagName") VALUES('5d90febd-6a6b-2c58-00c8-379c21acfc72', 'Rock music')
//SELECT a."SongId", a."TagId" //SELECT a."SongId", a."TagId"
//FROM "EAUNL_MTM_SONGTAG" a //FROM "SongTag" a
//WHERE (a."SongId" = '5d90fdb3-6a6b-2c58-00c8-37987f29b197') //WHERE (a."SongId" = '5d90fdb3-6a6b-2c58-00c8-37987f29b197')
//DELETE FROM "EAUNL_MTM_SONGTAG" WHERE ("SongId" = '5d90fdb3-6a6b-2c58-00c8-37987f29b197' AND "TagId" = '5d90fdb7-6a6b-2c58-00c8-37991ead4f05' OR "SongId" = '5d90fdb3-6a6b-2c58-00c8-37987f29b197' AND "TagId" = '5d90fdcc-6a6b-2c58-00c8-379b5af59d25') //DELETE FROM "SongTag" WHERE ("SongId" = '5d90fdb3-6a6b-2c58-00c8-37987f29b197' AND "TagId" = '5d90fdb7-6a6b-2c58-00c8-37991ead4f05' OR "SongId" = '5d90fdb3-6a6b-2c58-00c8-37987f29b197' AND "TagId" = '5d90fdcc-6a6b-2c58-00c8-379b5af59d25')
//INSERT INTO "EAUNL_MTM_SONGTAG"("SongId", "TagId") VALUES('5d90fdb3-6a6b-2c58-00c8-37987f29b197', '5d90febd-6a6b-2c58-00c8-379c21acfc72') //INSERT INTO "SongTag"("SongId", "TagId") VALUES('5d90fdb3-6a6b-2c58-00c8-37987f29b197', '5d90febd-6a6b-2c58-00c8-379c21acfc72')
ss[0].Name = "Take Me To Your Heart.mp4"; ss[0].Name = "love you forever.mp4";
ss[0].Tags.Clear(); ss[0].Tags.Clear();
ss[1].Name = "Kiss Me More.mp4"; ss[1].Name = "Li Bai.mp4";
ss[1].Tags.Clear(); ss[1].Tags.Clear();
repo.Update(ss); repo.Update(ss);
//DELETE FROM "EAUNL_MTM_SONGTAG" WHERE ("SongId" = '5d90fdb3-6a6b-2c58-00c8-37974177440d') //DELETE FROM "SongTag" WHERE ("SongId" = '5d90fdb3-6a6b-2c58-00c8-37974177440d')
//DELETE FROM "EAUNL_MTM_SONGTAG" WHERE ("SongId" = '5d90fdb3-6a6b-2c58-00c8-37987f29b197') //DELETE FROM "SongTag" WHERE ("SongId" = '5d90fdb3-6a6b-2c58-00c8-37987f29b197')
//UPDATE "EAUNL_MTM_SONG" SET "Name" = CASE "Id" //UPDATE "Song" SET "Name" = CASE "Id"
//WHEN '5d90fdb3-6a6b-2c58-00c8-37974177440d' THEN 'Take Me To Your Heart.mp4' //WHEN '5d90fdb3-6a6b-2c58-00c8-37974177440d' THEN 'love you forever.mp4'
//WHEN '5d90fdb3-6a6b-2c58-00c8-37987f29b197' THEN 'Kiss Me More.mp4' END //WHEN '5d90fdb3-6a6b-2c58-00c8-37987f29b197' THEN 'Li Bai.mp4' END
//WHERE ("Id" IN ('5d90fdb3-6a6b-2c58-00c8-37974177440d','5d90fdb3-6a6b-2c58-00c8-37987f29b197')) //WHERE ("Id" IN ('5d90fdb3-6a6b-2c58-00c8-37974177440d','5d90fdb3-6a6b-2c58-00c8-37987f29b197'))
} }
``` ```

@@ -118,7 +118,6 @@ repo.Insert(new UserGroup
//INSERT INTO "user"("username", "password", "groupid") VALUES('admin01', 'pwd01', 1), ('admin02', 'pwd02', 1), ('admin03', 'pwd03', 1) RETURNING "id" as "Id", "username" as "Username", "password" as "Password", "groupid" as "GroupId" //INSERT INTO "user"("username", "password", "groupid") VALUES('admin01', 'pwd01', 1), ('admin02', 'pwd02', 1), ('admin03', 'pwd03', 1) RETURNING "id" as "Id", "username" as "Username", "password" as "Password", "groupid" as "GroupId"
//INSERT INTO "userext"("userid", "remark") VALUES(3, '用户备注01'), (4, '用户备注02'), (5, '用户备注03') //INSERT INTO "userext"("userid", "remark") VALUES(3, '用户备注01'), (4, '用户备注02'), (5, '用户备注03')
var groups = repo.Select var groups = repo.Select
.IncludeMany(a => a.Users, .IncludeMany(a => a.Users,
then => then.Include(b => b.UserExt)) then => then.Include(b => b.UserExt))

@@ -21,7 +21,7 @@ repo.Insert(item);
repo.SaveMany(item, "Childs"); repo.SaveMany(item, "Childs");
``` ```
- 支持OneToMany】、【ManyToMany导航属性 - 支持 OneToManyManyToMany 导航属性
- 只保存 Childs不向下递归追朔 - 只保存 Childs不向下递归追朔
- 当 Childs 为 Empty 时,删除 item 存在的 Childs 所有表数据,确认? - 当 Childs 为 Empty 时,删除 item 存在的 Childs 所有表数据,确认?
- 优点:机制简单,好把控,安全 - 优点:机制简单,好把控,安全
@@ -29,13 +29,13 @@ repo.SaveMany(item, "Childs");
## EnableCascadeSave 仓储级联保存 ## EnableCascadeSave 仓储级联保存
DbContext/Repository EnableCascadeSave 可实现保存对象的时候,追朔其OneToOne】、【OneToMany】、【ManyToMany导航属性也一并保存,本文档说明实现的机制防止误用。 DbContext/Repository EnableCascadeSave 可实现保存对象的时候,递归追朔其 OneToOneOneToManyManyToMany 导航属性也一并保存,本文档说明实现的机制防止误用。
1、OneToOne 级联保存 1、OneToOne 级联保存
> v3.2.606+ 支持,并且支持级联删除功能 > v3.2.606+ 支持,并且支持[级联删除功能](https://github.com/dotnetcore/FreeSql/wiki/%e5%88%a0%e9%99%a4#ibaserepository-%E7%BA%A7%E8%81%94%E5%88%A0%E9%99%A4)
2、OneToMany 追加保存,不删除表已存在的数据 2、OneToMany 追加或更新子表,不删除表已存在的数据
```csharp ```csharp
var repo = fsql.GetRepository<Cagetory>(); var repo = fsql.GetRepository<Cagetory>();
@@ -43,7 +43,7 @@ repo.DbContextOptions.EnableCascadeSave = true; //需要手工开启
repo.Insert(item); repo.Insert(item);
``` ```
- 不删除表已存在的数据,确认? - 不删除 Childs 子表已存在的数据,确认?
- 当 Childs 属性为 Empty 时,不做任何操作,确认? - 当 Childs 属性为 Empty 时,不做任何操作,确认?
- 保存 Childs 的时候,还会保存 Childs\[0-..\] 的下级集合属性向下18层确认 - 保存 Childs 的时候,还会保存 Childs\[0-..\] 的下级集合属性向下18层确认
@@ -51,11 +51,15 @@ repo.Insert(item);
> 保存【类型】表对象的时候,他会向下检索出集合属性【文章】,然后如果【文章】被保存的时候,再继续向下检索出集合属性【评论】。一起做 InsertOrUpdate 操作。 > 保存【类型】表对象的时候,他会向下检索出集合属性【文章】,然后如果【文章】被保存的时候,再继续向下检索出集合属性【评论】。一起做 InsertOrUpdate 操作。
3、ManyToMany 完整保存 3、ManyToMany 完整对比保存中间表,追加外部表
完整保存,对比【多对多】中间表已存在的数据,计算出添加、修改、删除执行。 完整对比保存中间表,对比【多对多】中间表已存在的数据,计算出添加、修改、删除执行。
> ManyToMany 模型下,开启 EnableCascadeSave 或者 SaveMany 都是完整保存 追加外部表,只追加不更新
- 本表 Song
- 外部表 Tag
- 中间表 SongTag
--- ---