update

28810
2019-09-29 15:09:24 +08:00
parent e63a1189bc
commit 4aabe29343
5 changed files with 238 additions and 40 deletions

@@ -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)

@@ -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

@@ -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 };
```