mirror of
https://github.com/dotnetcore/FreeSql.git
synced 2026-03-06 22:20:57 +08:00
update
41
DbContext.md
41
DbContext.md
@@ -160,47 +160,8 @@ Guid Id 的情况下,执行三次命令:前两次插入合并执行,update
|
||||
|
||||
## 联级保存
|
||||
|
||||
```csharp
|
||||
class Cagetory
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
请移步文档[《联级保存》](https://github.com/2881099/FreeSql/wiki/%e8%81%94%e7%ba%a7%e4%bf%9d%e5%ad%98)
|
||||
|
||||
[Navigate("CagetoryId")]
|
||||
public List<Goods> Goodss { get; set; }
|
||||
}
|
||||
class Goods
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid CagetoryId { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
上面是【一对多】模型, Catetory 保存时可联级保存 Goodss 集合。出于使用安全考虑我们没做完整对比,只实现 Goodss 集合的添加或更新操作,所以不会删除 Goods 的数据。
|
||||
|
||||
完整对比的功能使用起来太危险,试想下面的场景:
|
||||
|
||||
- 保存 Cagetory 的时候,Goodss 是个空列表,如何操作?记录全部删除?
|
||||
- 保存 Category 的时候,由于数据库中 Goodss 记录非常之多,那么只想保存 Goodss 部分数据,或者只需要添加,如何操作?
|
||||
|
||||
【多对多】模型下,我们对中间表的保存是完整对比操作,对外部实体的操作只作新增(注意不会更新)
|
||||
|
||||
- 属性集合为空时,删除他们的所有关联数据(中间表)
|
||||
- 属性集合不为空时,与数据库存在的数据完全对比,计算出应该删除和添加的记录
|
||||
|
||||
如何关闭联级保存功能?
|
||||
|
||||
全局关闭:
|
||||
|
||||
```csharp
|
||||
fsql.SetDbContextOptions(opt => opt.EnableAddOrUpdateNavigateList = false);
|
||||
```
|
||||
|
||||
局部关闭:
|
||||
```csharp
|
||||
var repo = fsql.GetRepository<T>();
|
||||
repo.DbContextOptions = new DbContextOptions { EnableAddOrUpdateNavigateList = false };
|
||||
```
|
||||
## 参考资料
|
||||
|
||||
- [《分区、分表、分库》](https://github.com/2881099/FreeSql/wiki/%e5%88%86%e5%8c%ba%e5%88%86%e8%a1%a8)
|
||||
|
||||
@@ -139,6 +139,10 @@ using (var uow = fsql.CreateUnitOfWork()) {
|
||||
}
|
||||
```
|
||||
|
||||
## 联级保存
|
||||
|
||||
请移步文档[《联级保存》](https://github.com/2881099/FreeSql/wiki/%e8%81%94%e7%ba%a7%e4%bf%9d%e5%ad%98)
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [《学习FreeSql之一:添加数据》](https://github.com/2881099/FreeSql/wiki/%e6%b7%bb%e5%8a%a0)
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
* [仓储层Repository](https://github.com/2881099/FreeSql/wiki/Repository)
|
||||
* [过滤器](https://github.com/2881099/FreeSql/wiki/%e8%bf%87%e6%bb%a4%e5%99%a8)
|
||||
* [UnitOfWork](https://github.com/2881099/FreeSql/wiki/%e5%b7%a5%e4%bd%9c%e5%8d%95%e5%85%83)
|
||||
* [联级保存](https://github.com/2881099/FreeSql/wiki/%e8%81%94%e7%ba%a7%e4%bf%9d%e5%ad%98)
|
||||
* [DbContext](https://github.com/2881099/FreeSql/wiki/DbContext)
|
||||
* [CodeFirst](https://github.com/2881099/FreeSql/wiki/CodeFirst)
|
||||
* [实体特性!!](https://github.com/2881099/FreeSql/wiki/%e5%ae%9e%e4%bd%93%e7%89%b9%e6%80%a7)
|
||||
|
||||
4
更新日志.md
4
更新日志.md
@@ -1,6 +1,10 @@
|
||||
|
||||
完整版本:年数-月-日-当日版本号,FreeSql、FreeSql.Repository、FreeSql.DbContext 版本号相同。
|
||||
|
||||
## v0.10.5
|
||||
|
||||
- 增加 DbContext/Repository ManyToMany联级保存功能(之前已支持OneToMany);
|
||||
|
||||
## v0.10.4
|
||||
|
||||
- 增加 ColumnAttribute 可插入(CanInsert)、可更新(CanUpdate);#99
|
||||
|
||||
228
联级保存.md
Normal file
228
联级保存.md
Normal file
@@ -0,0 +1,228 @@
|
||||
联级保存可实现保存对象的时候,将其【OneyToMany】、【ManyToMany】导航属性集合也一并保存,本文档说明实现的机制防止误用。
|
||||
|
||||
## OneToMany 联级保存
|
||||
|
||||
```csharp
|
||||
class Cagetory
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
[Navigate("CagetoryId")]
|
||||
public List<Goods> Goodss { get; set; }
|
||||
}
|
||||
class Goods
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid CagetoryId { get; set; }
|
||||
}
|
||||
```
|
||||
|
||||
上面是【一对多】模型, Catetory 保存时可联级保存 Goodss 集合。出于使用安全考虑我们没做完整对比,只实现 Goodss 集合的添加或更新操作,所以不会删除 Goods 的数据。
|
||||
|
||||
完整对比的功能使用起来太危险,试想下面的场景:
|
||||
|
||||
- 保存 Cagetory 的时候,Goodss 是个空列表,如何操作?记录全部删除?
|
||||
- 保存 Category 的时候,由于数据库中 Goodss 记录非常之多,那么只想保存 Goodss 部分数据,或者只需要添加,如何操作?
|
||||
|
||||
测试父子关系:
|
||||
```csharp
|
||||
[Table(Name = "EAUNL_OTMP_CT")]
|
||||
class CagetoryParent
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
|
||||
public Guid ParentId { get; set; }
|
||||
[Navigate("ParentId")]
|
||||
public List<CagetoryParent> Childs { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnableAddOrUpdateNavigateList_OneToMany_Parent()
|
||||
{
|
||||
var repo = g.sqlite.GetRepository<CagetoryParent>();
|
||||
var cts = new[] {
|
||||
new CagetoryParent
|
||||
{
|
||||
Name = "分类1",
|
||||
Childs = new List<CagetoryParent>(new[]
|
||||
{
|
||||
new CagetoryParent { Name = "分类1_1" },
|
||||
new CagetoryParent { Name = "分类1_2" },
|
||||
new CagetoryParent { Name = "分类1_3" }
|
||||
})
|
||||
},
|
||||
new CagetoryParent
|
||||
{
|
||||
Name = "分类2",
|
||||
Childs = new List<CagetoryParent>(new[]
|
||||
{
|
||||
new CagetoryParent { Name = "分类2_1" },
|
||||
new CagetoryParent { Name = "分类2_2" }
|
||||
})
|
||||
}
|
||||
};
|
||||
repo.Insert(cts);
|
||||
//执行创建表,和插入数据:
|
||||
//INSERT INTO "EAUNL_OTMP_CT"("Id", "Name", "ParentId") VALUES('5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f', '分类1', '00000000-0000-0000-0000-000000000000'), ('5d90afcb-ed57-f6f4-0082-cb6c5b531b3e', '分类2', '00000000-0000-0000-0000-000000000000')
|
||||
//INSERT INTO "EAUNL_OTMP_CT"("Id", "Name", "ParentId") VALUES('5d90afcb-ed57-f6f4-0082-cb6d0c1c5f1a', '分类1_1', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afcb-ed57-f6f4-0082-cb6e74bd8eef', '分类1_2', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afcb-ed57-f6f4-0082-cb6f6267cc5f', '分类1_3', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afcb-ed57-f6f4-0082-cb7057c41d46', '分类2_1', '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e'), ('5d90afcb-ed57-f6f4-0082-cb7156e0375e', '分类2_2', '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e')
|
||||
cts[0].Name = "分类11";
|
||||
cts[0].Childs.Clear();
|
||||
cts[1].Name = "分类22";
|
||||
cts[1].Childs.Clear();
|
||||
repo.Update(cts);
|
||||
//UPDATE "EAUNL_OTMP_CT" SET "Name" = CASE "Id"
|
||||
//WHEN '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f' THEN '分类11'
|
||||
//WHEN '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e' THEN '分类22' END
|
||||
//WHERE ("Id" IN ('5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f','5d90afcb-ed57-f6f4-0082-cb6c5b531b3e'))
|
||||
//Goodss.Clear 后没有执行删除子集合操作,说明没有做完整的对比
|
||||
cts[0].Name = "分类111";
|
||||
cts[0].Childs.Clear();
|
||||
cts[0].Childs.Add(new CagetoryParent { Name = "分类1_33" });
|
||||
cts[1].Name = "分类222";
|
||||
cts[1].Childs.Clear();
|
||||
cts[1].Childs.Add(new CagetoryParent { Name = "分类2_22" });
|
||||
repo.Update(cts);
|
||||
//UPDATE "EAUNL_OTMP_CT" SET "Name" = CASE "Id"
|
||||
//WHEN '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f' THEN '分类111'
|
||||
//WHEN '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e' THEN '分类222' END
|
||||
//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', '分类1_33', '5d90afcb-ed57-f6f4-0082-cb6b78eaaf9f'), ('5d90afe8-ed57-f6f4-0082-cb7338a6214c', '分类2_22', '5d90afcb-ed57-f6f4-0082-cb6c5b531b3e')
|
||||
}
|
||||
```
|
||||
|
||||
## ManyToMany 联级保存
|
||||
|
||||
【多对多】模型下,我们对中间表的保存是完整对比操作,对外部实体的操作只作新增(注意不会更新)
|
||||
|
||||
- 属性集合为空时,删除他们的所有关联数据(中间表)
|
||||
- 属性集合不为空时,与数据库存在的关联数据(中间表)完全对比,计算出应该删除和添加的记录
|
||||
|
||||
```csharp
|
||||
[Table(Name = "EAUNL_MTM_SONG")]
|
||||
class Song
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public List<Tag> Tags { get; set; }
|
||||
}
|
||||
[Table(Name = "EAUNL_MTM_TAG")]
|
||||
class Tag
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public string TagName { get; set; }
|
||||
public List<Song> Songs { get; set; }
|
||||
}
|
||||
[Table(Name = "EAUNL_MTM_SONGTAG")]
|
||||
class SongTag
|
||||
{
|
||||
public Guid SongId { get; set; }
|
||||
public Song Song { get; set; }
|
||||
public Guid TagId { get; set; }
|
||||
public Tag Tag { get; set; }
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnableAddOrUpdateNavigateList_ManyToMany()
|
||||
{
|
||||
var tags = new[] {
|
||||
new Tag { TagName = "流行" },
|
||||
new Tag { TagName = "80后" },
|
||||
new Tag { TagName = "00后" },
|
||||
new Tag { TagName = "摇滚" }
|
||||
};
|
||||
var ss = new[]
|
||||
{
|
||||
new Song
|
||||
{
|
||||
Name = "爱你一万年.mp3",
|
||||
Tags = new List<Tag>(new[]
|
||||
{
|
||||
tags[0], tags[1]
|
||||
})
|
||||
},
|
||||
new Song
|
||||
{
|
||||
Name = "李白.mp3",
|
||||
Tags = new List<Tag>(new[]
|
||||
{
|
||||
tags[0], tags[2]
|
||||
})
|
||||
}
|
||||
};
|
||||
var repo = g.sqlite.GetRepository<Song>();
|
||||
repo.Insert(ss);
|
||||
//INSERT INTO "EAUNL_MTM_SONG"("Id", "Name") VALUES('5d90c2dc-f28f-705c-0033-bf956ea510aa', '爱你一万年.mp3'), ('5d90c2dc-f28f-705c-0033-bf962070dfa1', '李白.mp3')
|
||||
|
||||
//INSERT INTO "EAUNL_MTM_TAG"("Id", "TagName") VALUES('5d90c2dc-f28f-705c-0033-bf970d44be78', '流行'), ('5d90c2dc-f28f-705c-0033-bf980d45b140', '80后')
|
||||
|
||||
//SELECT a."SongId", a."TagId"
|
||||
//FROM "EAUNL_MTM_SONGTAG" a
|
||||
//WHERE (a."SongId" = '5d90c2dc-f28f-705c-0033-bf956ea510aa')
|
||||
|
||||
//INSERT INTO "EAUNL_MTM_SONGTAG"("SongId", "TagId") VALUES('5d90c2dc-f28f-705c-0033-bf956ea510aa', '5d90c2dc-f28f-705c-0033-bf970d44be78'), ('5d90c2dc-f28f-705c-0033-bf956ea510aa', '5d90c2dc-f28f-705c-0033-bf980d45b140')
|
||||
|
||||
//INSERT INTO "EAUNL_MTM_TAG"("Id", "TagName") VALUES('5d90c2dc-f28f-705c-0033-bf9945ace3b5', '00后')
|
||||
|
||||
//SELECT a."SongId", a."TagId"
|
||||
//FROM "EAUNL_MTM_SONGTAG" a
|
||||
//WHERE (a."SongId" = '5d90c2dc-f28f-705c-0033-bf962070dfa1')
|
||||
|
||||
//INSERT INTO "EAUNL_MTM_SONGTAG"("SongId", "TagId") VALUES('5d90c2dc-f28f-705c-0033-bf962070dfa1', '5d90c2dc-f28f-705c-0033-bf970d44be78'), ('5d90c2dc-f28f-705c-0033-bf962070dfa1', '5d90c2dc-f28f-705c-0033-bf9945ace3b5')
|
||||
|
||||
ss[0].Name = "爱你一万年.mp5";
|
||||
ss[0].Tags.Clear();
|
||||
ss[0].Tags.Add(tags[0]);
|
||||
ss[1].Name = "李白.mp5";
|
||||
ss[1].Tags.Clear();
|
||||
ss[1].Tags.Add(tags[3]);
|
||||
repo.Update(ss);
|
||||
//UPDATE "EAUNL_MTM_SONG" SET "Name" = CASE "Id"
|
||||
//WHEN '5d90c2dc-f28f-705c-0033-bf956ea510aa' THEN '爱你一万年.mp5'
|
||||
//WHEN '5d90c2dc-f28f-705c-0033-bf962070dfa1' THEN '李白.mp5' END
|
||||
//WHERE ("Id" IN ('5d90c2dc-f28f-705c-0033-bf956ea510aa','5d90c2dc-f28f-705c-0033-bf962070dfa1'))
|
||||
|
||||
//SELECT a."SongId", a."TagId"
|
||||
//FROM "EAUNL_MTM_SONGTAG" a
|
||||
//WHERE (a."SongId" = '5d90c2dc-f28f-705c-0033-bf956ea510aa')
|
||||
|
||||
//DELETE FROM "EAUNL_MTM_SONGTAG" WHERE ("SongId" = '5d90c2dc-f28f-705c-0033-bf956ea510aa' AND "TagId" = '5d90c2dc-f28f-705c-0033-bf980d45b140')
|
||||
|
||||
//INSERT INTO "EAUNL_MTM_TAG"("Id", "TagName") VALUES('5d90c2e7-f28f-705c-0033-bf9a0148ee6f', '摇滚')
|
||||
|
||||
//SELECT a."SongId", a."TagId"
|
||||
//FROM "EAUNL_MTM_SONGTAG" a
|
||||
//WHERE (a."SongId" = '5d90c2dc-f28f-705c-0033-bf962070dfa1')
|
||||
|
||||
//DELETE FROM "EAUNL_MTM_SONGTAG" WHERE ("SongId" = '5d90c2dc-f28f-705c-0033-bf962070dfa1' AND "TagId" = '5d90c2dc-f28f-705c-0033-bf970d44be78' OR "SongId" = '5d90c2dc-f28f-705c-0033-bf962070dfa1' AND "TagId" = '5d90c2dc-f28f-705c-0033-bf9945ace3b5')
|
||||
|
||||
//INSERT INTO "EAUNL_MTM_SONGTAG"("SongId", "TagId") VALUES('5d90c2dc-f28f-705c-0033-bf962070dfa1', '5d90c2e7-f28f-705c-0033-bf9a0148ee6f')
|
||||
|
||||
ss[0].Name = "爱你一万年.mp4";
|
||||
ss[0].Tags.Clear();
|
||||
ss[1].Name = "李白.mp4";
|
||||
ss[1].Tags.Clear();
|
||||
repo.Update(ss);
|
||||
//DELETE FROM "EAUNL_MTM_SONGTAG" WHERE ("SongId" = '5d90c2dc-f28f-705c-0033-bf956ea510aa')
|
||||
//DELETE FROM "EAUNL_MTM_SONGTAG" WHERE ("SongId" = '5d90c2dc-f28f-705c-0033-bf962070dfa1')
|
||||
|
||||
//UPDATE "EAUNL_MTM_SONG" SET "Name" = CASE "Id"
|
||||
//WHEN '5d90c2dc-f28f-705c-0033-bf956ea510aa' THEN '爱你一万年.mp4'
|
||||
//WHEN '5d90c2dc-f28f-705c-0033-bf962070dfa1' THEN '李白.mp4' END
|
||||
//WHERE ("Id" IN ('5d90c2dc-f28f-705c-0033-bf956ea510aa','5d90c2dc-f28f-705c-0033-bf962070dfa1'))
|
||||
}
|
||||
```
|
||||
如何关闭联级保存功能?
|
||||
|
||||
全局关闭:
|
||||
|
||||
```csharp
|
||||
fsql.SetDbContextOptions(opt => opt.EnableAddOrUpdateNavigateList = false);
|
||||
```
|
||||
|
||||
局部关闭:
|
||||
```csharp
|
||||
var repo = fsql.GetRepository<T>();
|
||||
repo.DbContextOptions = new DbContextOptions { EnableAddOrUpdateNavigateList = false };
|
||||
```
|
||||
Reference in New Issue
Block a user