mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-06-18 20:08:15 +08:00
- 增加 Repository/DbContext SaveMany 方法实现一对多,子数据的完整保存;
- 调整 SaveManyToMany 方法名为 SaveMany;
This commit is contained in:
@ -158,14 +158,18 @@ namespace FreeSql
|
||||
this.Set<TEntity>().AddOrUpdate(data);
|
||||
}
|
||||
/// <summary>
|
||||
/// 保存实体的指定 ManyToMany 导航属性
|
||||
/// 保存实体的指定 ManyToMany/OneToMany 导航属性(完整对比)<para></para>
|
||||
/// 场景:在关闭级联保存功能之后,手工使用本方法<para></para>
|
||||
/// 例子:保存商品的 OneToMany 集合属性,SaveMany(goods, "Skus")<para></para>
|
||||
/// 当 goods.Skus 为空(非null)时,会删除表中已存在的所有数据<para></para>
|
||||
/// 当 goods.Skus 不为空(非null)时,添加/更新后,删除表中不存在 Skus 集合属性的所有记录
|
||||
/// </summary>
|
||||
/// <param name="data">实体对象</param>
|
||||
/// <param name="propertyName">属性名</param>
|
||||
public void SaveManyToMany<TEntity>(TEntity data, string propertyName) where TEntity : class
|
||||
public void SaveMany<TEntity>(TEntity data, string propertyName) where TEntity : class
|
||||
{
|
||||
CheckEntityTypeOrThrow(typeof(TEntity));
|
||||
this.Set<TEntity>().SaveManyToMany(data, propertyName);
|
||||
this.Set<TEntity>().SaveMany(data, propertyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -212,10 +216,10 @@ namespace FreeSql
|
||||
CheckEntityTypeOrThrow(typeof(TEntity));
|
||||
return this.Set<TEntity>().AddOrUpdateAsync(data);
|
||||
}
|
||||
public Task SaveManyToManyAsync<TEntity>(TEntity data, string propertyName) where TEntity : class
|
||||
public Task SaveManyAsync<TEntity>(TEntity data, string propertyName) where TEntity : class
|
||||
{
|
||||
CheckEntityTypeOrThrow(typeof(TEntity));
|
||||
return this.Set<TEntity>().SaveManyToManyAsync(data, propertyName);
|
||||
return this.Set<TEntity>().SaveManyAsync(data, propertyName);
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
@ -9,9 +9,9 @@ namespace FreeSql
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 是否开启一对多,多对多联级保存功能<para></para>
|
||||
/// 是否开启一对多,多对多级联保存功能<para></para>
|
||||
/// <para></para>
|
||||
/// 【一对多】模型下, 保存时可联级保存实体的属性集合。出于使用安全考虑我们没做完整对比,只实现实体属性集合的添加或更新操作,所以不会删除实体属性集合的数据。<para></para>
|
||||
/// 【一对多】模型下, 保存时可级联保存实体的属性集合。出于使用安全考虑我们没做完整对比,只实现实体属性集合的添加或更新操作,所以不会删除实体属性集合的数据。<para></para>
|
||||
/// 完整对比的功能使用起来太危险,试想下面的场景:<para></para>
|
||||
/// - 保存的时候,实体的属性集合是空的,如何操作?记录全部删除?<para></para>
|
||||
/// - 保存的时候,由于数据库中记录非常之多,那么只想保存子表的部分数据,或者只需要添加,如何操作?<para></para>
|
||||
|
@ -133,22 +133,44 @@ namespace FreeSql
|
||||
}
|
||||
}
|
||||
|
||||
async public Task SaveManyToManyAsync(TEntity item, string propertyName)
|
||||
async public Task SaveManyAsync(TEntity item, string propertyName)
|
||||
{
|
||||
if (item == null) return;
|
||||
if (_table.Properties.ContainsKey(propertyName) == false) throw new KeyNotFoundException($"{_table.Type.FullName} 不存在属性 {propertyName}");
|
||||
if (string.IsNullOrEmpty(propertyName)) return;
|
||||
if (_table.Properties.TryGetValue(propertyName, out var prop) == false) throw new KeyNotFoundException($"{_table.Type.FullName} 不存在属性 {propertyName}");
|
||||
if (_table.ColumnsByCsIgnore.ContainsKey(propertyName)) throw new ArgumentException($"{_table.Type.FullName} 类型已设置属性 {propertyName} 忽略特性");
|
||||
|
||||
var tref = _table.GetTableRef(propertyName, true);
|
||||
if (tref.RefType != Internal.Model.TableRefType.ManyToMany) throw new ArgumentException($"{_table.Type.FullName} 类型的属性 {propertyName} 不是 ManyToMany 特性");
|
||||
DbContextExecCommand();
|
||||
if (tref == null) return;
|
||||
switch (tref.RefType)
|
||||
{
|
||||
case Internal.Model.TableRefType.OneToOne:
|
||||
case Internal.Model.TableRefType.ManyToOne:
|
||||
throw new ArgumentException($"{_table.Type.FullName} 类型的属性 {propertyName} 不是 OneToMany 或 ManyToMany 特性");
|
||||
}
|
||||
|
||||
await DbContextExecCommandAsync();
|
||||
var oldEnable = _db.Options.EnableAddOrUpdateNavigateList;
|
||||
_db.Options.EnableAddOrUpdateNavigateList = false;
|
||||
await AddOrUpdateNavigateListAsync(item, false, propertyName);
|
||||
_db.Options.EnableAddOrUpdateNavigateList = oldEnable;
|
||||
try
|
||||
{
|
||||
await AddOrUpdateNavigateListAsync(item, false, propertyName);
|
||||
if (tref.RefType == Internal.Model.TableRefType.OneToMany)
|
||||
{
|
||||
await DbContextExecCommandAsync();
|
||||
//删除没有保存的数据
|
||||
var propValEach = GetItemValue(item, prop) as IEnumerable;
|
||||
await _db.Orm.Select<object>().AsType(tref.RefEntityType).WhereDynamic(propValEach, true)
|
||||
.ToDelete().ExecuteAffrowsAsync();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_db.Options.EnableAddOrUpdateNavigateList = oldEnable;
|
||||
}
|
||||
}
|
||||
async Task AddOrUpdateNavigateListAsync(TEntity item, bool isAdd, string propertyName = null)
|
||||
{
|
||||
Type itemType = null;
|
||||
Func<PropertyInfo, Task> action = async prop =>
|
||||
{
|
||||
if (_table.ColumnsByCsIgnore.ContainsKey(prop.Name)) return;
|
||||
@ -163,26 +185,7 @@ namespace FreeSql
|
||||
return;
|
||||
}
|
||||
|
||||
object propVal = null;
|
||||
if (itemType == null) itemType = item.GetType();
|
||||
if (_table.TypeLazy != null && itemType == _table.TypeLazy)
|
||||
{
|
||||
var lazyField = _dicLazyIsSetField.GetOrAdd(_table.TypeLazy, tl => new ConcurrentDictionary<string, FieldInfo>()).GetOrAdd(prop.Name, propName =>
|
||||
_table.TypeLazy.GetField($"__lazy__{propName}", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance));
|
||||
if (lazyField != null)
|
||||
{
|
||||
var lazyFieldValue = (bool)lazyField.GetValue(item);
|
||||
if (lazyFieldValue == false) return;
|
||||
}
|
||||
propVal = prop.GetValue(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
propVal = prop.GetValue(item);
|
||||
if (propVal == null) return;
|
||||
}
|
||||
|
||||
var propValEach = propVal as IEnumerable;
|
||||
var propValEach = GetItemValue(item, prop) as IEnumerable;
|
||||
if (propValEach == null) return;
|
||||
DbSet<object> refSet = GetDbSetObject(tref.RefEntityType);
|
||||
switch (tref.RefType)
|
||||
|
@ -136,34 +136,53 @@ namespace FreeSql
|
||||
}
|
||||
}
|
||||
|
||||
static ConcurrentDictionary<Type, ConcurrentDictionary<string, FieldInfo>> _dicLazyIsSetField = new ConcurrentDictionary<Type, ConcurrentDictionary<string, FieldInfo>>();
|
||||
/// <summary>
|
||||
/// 保存实体的指定 ManyToMany 导航属性
|
||||
/// 保存实体的指定 ManyToMany/OneToMany 导航属性(完整对比)<para></para>
|
||||
/// 场景:在关闭级联保存功能之后,手工使用本方法<para></para>
|
||||
/// 例子:保存商品的 OneToMany 集合属性,SaveMany(goods, "Skus")<para></para>
|
||||
/// 当 goods.Skus 为空(非null)时,会删除表中已存在的所有数据<para></para>
|
||||
/// 当 goods.Skus 不为空(非null)时,添加/更新后,删除表中不存在 Skus 集合属性的所有记录
|
||||
/// </summary>
|
||||
/// <param name="item">实体对象</param>
|
||||
/// <param name="propertyName">属性名</param>
|
||||
public void SaveManyToMany(TEntity item, string propertyName)
|
||||
public void SaveMany(TEntity item, string propertyName)
|
||||
{
|
||||
if (item == null) return;
|
||||
if (_table.Properties.ContainsKey(propertyName) == false) throw new KeyNotFoundException($"{_table.Type.FullName} 不存在属性 {propertyName}");
|
||||
if (string.IsNullOrEmpty(propertyName)) return;
|
||||
if (_table.Properties.TryGetValue(propertyName, out var prop) == false) throw new KeyNotFoundException($"{_table.Type.FullName} 不存在属性 {propertyName}");
|
||||
if (_table.ColumnsByCsIgnore.ContainsKey(propertyName)) throw new ArgumentException($"{_table.Type.FullName} 类型已设置属性 {propertyName} 忽略特性");
|
||||
|
||||
var tref = _table.GetTableRef(propertyName, true);
|
||||
if (tref.RefType != Internal.Model.TableRefType.ManyToMany) throw new ArgumentException($"{_table.Type.FullName} 类型的属性 {propertyName} 不是 ManyToMany 特性");
|
||||
if (tref == null) return;
|
||||
switch (tref.RefType)
|
||||
{
|
||||
case Internal.Model.TableRefType.OneToOne:
|
||||
case Internal.Model.TableRefType.ManyToOne:
|
||||
throw new ArgumentException($"{_table.Type.FullName} 类型的属性 {propertyName} 不是 OneToMany 或 ManyToMany 特性");
|
||||
}
|
||||
|
||||
DbContextExecCommand();
|
||||
var oldEnable = _db.Options.EnableAddOrUpdateNavigateList;
|
||||
_db.Options.EnableAddOrUpdateNavigateList = false;
|
||||
AddOrUpdateNavigateList(item, false, propertyName);
|
||||
_db.Options.EnableAddOrUpdateNavigateList = oldEnable;
|
||||
try
|
||||
{
|
||||
AddOrUpdateNavigateList(item, false, propertyName);
|
||||
if (tref.RefType == Internal.Model.TableRefType.OneToMany)
|
||||
{
|
||||
DbContextExecCommand();
|
||||
//删除没有保存的数据
|
||||
var propValEach = GetItemValue(item, prop) as IEnumerable;
|
||||
_db.Orm.Select<object>().AsType(tref.RefEntityType).WhereDynamic(propValEach, true)
|
||||
.ToDelete().ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_db.Options.EnableAddOrUpdateNavigateList = oldEnable;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 联级保存导航集合
|
||||
/// </summary>
|
||||
/// <param name="item">实体对象</param>
|
||||
/// <param name="isAdd">是否为新增的实体对象</param>
|
||||
/// <param name="propertyName">指定保存的属性</param>
|
||||
void AddOrUpdateNavigateList(TEntity item, bool isAdd, string propertyName = null)
|
||||
{
|
||||
Type itemType = null;
|
||||
Action<PropertyInfo> action = prop =>
|
||||
{
|
||||
if (_table.ColumnsByCsIgnore.ContainsKey(prop.Name)) return;
|
||||
@ -178,26 +197,7 @@ namespace FreeSql
|
||||
return;
|
||||
}
|
||||
|
||||
object propVal = null;
|
||||
if (itemType == null) itemType = item.GetType();
|
||||
if (_table.TypeLazy != null && itemType == _table.TypeLazy)
|
||||
{
|
||||
var lazyField = _dicLazyIsSetField.GetOrAdd(_table.TypeLazy, tl => new ConcurrentDictionary<string, FieldInfo>()).GetOrAdd(prop.Name, propName =>
|
||||
_table.TypeLazy.GetField($"__lazy__{propName}", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance));
|
||||
if (lazyField != null)
|
||||
{
|
||||
var lazyFieldValue = (bool)lazyField.GetValue(item);
|
||||
if (lazyFieldValue == false) return;
|
||||
}
|
||||
propVal = prop.GetValue(item, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
propVal = prop.GetValue(item, null);
|
||||
if (propVal == null) return;
|
||||
}
|
||||
|
||||
var propValEach = propVal as IEnumerable;
|
||||
var propValEach = GetItemValue(item, prop) as IEnumerable;
|
||||
if (propValEach == null) return;
|
||||
DbSet<object> refSet = GetDbSetObject(tref.RefEntityType);
|
||||
switch (tref.RefType)
|
||||
@ -313,6 +313,29 @@ namespace FreeSql
|
||||
else if (_table.Properties.TryGetValue(propertyName, out var prop))
|
||||
action(prop);
|
||||
}
|
||||
static ConcurrentDictionary<Type, ConcurrentDictionary<string, FieldInfo>> _dicLazyIsSetField = new ConcurrentDictionary<Type, ConcurrentDictionary<string, FieldInfo>>();
|
||||
object GetItemValue(TEntity item, PropertyInfo prop)
|
||||
{
|
||||
object propVal = null;
|
||||
var itemType = item.GetType();
|
||||
if (_table.TypeLazy != null && itemType == _table.TypeLazy)
|
||||
{
|
||||
var lazyField = _dicLazyIsSetField.GetOrAdd(_table.TypeLazy, tl => new ConcurrentDictionary<string, FieldInfo>()).GetOrAdd(prop.Name, propName =>
|
||||
_table.TypeLazy.GetField($"__lazy__{propName}", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance));
|
||||
if (lazyField != null)
|
||||
{
|
||||
var lazyFieldValue = (bool)lazyField.GetValue(item);
|
||||
if (lazyFieldValue == false) return null;
|
||||
}
|
||||
propVal = prop.GetValue(item, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
propVal = prop.GetValue(item, null);
|
||||
if (propVal == null) return null;
|
||||
}
|
||||
return propVal;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Update
|
||||
|
@ -32,9 +32,13 @@
|
||||
<typeparam name="TEntity"></typeparam>
|
||||
<param name="data"></param>
|
||||
</member>
|
||||
<member name="M:FreeSql.DbContext.SaveManyToMany``1(``0,System.String)">
|
||||
<member name="M:FreeSql.DbContext.SaveMany``1(``0,System.String)">
|
||||
<summary>
|
||||
保存实体的指定 ManyToMany 导航属性
|
||||
保存实体的指定 ManyToMany/OneToMany 导航属性(完整对比)<para></para>
|
||||
场景:在关闭级联保存功能之后,手工使用本方法<para></para>
|
||||
例子:保存商品的 OneToMany 集合属性,SaveMany(goods, "Skus")<para></para>
|
||||
当 goods.Skus 为空(非null)时,会删除表中已存在的所有数据<para></para>
|
||||
当 goods.Skus 不为空(非null)时,添加/更新后,删除表中不存在 Skus 集合属性的所有记录
|
||||
</summary>
|
||||
<param name="data">实体对象</param>
|
||||
<param name="propertyName">属性名</param>
|
||||
@ -65,9 +69,9 @@
|
||||
</member>
|
||||
<member name="P:FreeSql.DbContextOptions.EnableAddOrUpdateNavigateList">
|
||||
<summary>
|
||||
是否开启一对多,多对多联级保存功能<para></para>
|
||||
是否开启一对多,多对多级联保存功能<para></para>
|
||||
<para></para>
|
||||
【一对多】模型下, 保存时可联级保存实体的属性集合。出于使用安全考虑我们没做完整对比,只实现实体属性集合的添加或更新操作,所以不会删除实体属性集合的数据。<para></para>
|
||||
【一对多】模型下, 保存时可级联保存实体的属性集合。出于使用安全考虑我们没做完整对比,只实现实体属性集合的添加或更新操作,所以不会删除实体属性集合的数据。<para></para>
|
||||
完整对比的功能使用起来太危险,试想下面的场景:<para></para>
|
||||
- 保存的时候,实体的属性集合是空的,如何操作?记录全部删除?<para></para>
|
||||
- 保存的时候,由于数据库中记录非常之多,那么只想保存子表的部分数据,或者只需要添加,如何操作?<para></para>
|
||||
@ -106,34 +110,23 @@
|
||||
清空状态数据
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:FreeSql.DbSet`1.RemoveAsync(System.Linq.Expressions.Expression{System.Func{`0,System.Boolean}})">
|
||||
<summary>
|
||||
根据 lambda 条件删除数据
|
||||
</summary>
|
||||
<param name="predicate"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:FreeSql.DbSet`1.Add(`0)">
|
||||
<summary>
|
||||
添加
|
||||
</summary>
|
||||
<param name="data"></param>
|
||||
</member>
|
||||
<member name="M:FreeSql.DbSet`1.SaveManyToMany(`0,System.String)">
|
||||
<member name="M:FreeSql.DbSet`1.SaveMany(`0,System.String)">
|
||||
<summary>
|
||||
保存实体的指定 ManyToMany 导航属性
|
||||
保存实体的指定 ManyToMany/OneToMany 导航属性(完整对比)<para></para>
|
||||
场景:在关闭级联保存功能之后,手工使用本方法<para></para>
|
||||
例子:保存商品的 OneToMany 集合属性,SaveMany(goods, "Skus")<para></para>
|
||||
当 goods.Skus 为空(非null)时,会删除表中已存在的所有数据<para></para>
|
||||
当 goods.Skus 不为空(非null)时,添加/更新后,删除表中不存在 Skus 集合属性的所有记录
|
||||
</summary>
|
||||
<param name="item">实体对象</param>
|
||||
<param name="propertyName">属性名</param>
|
||||
</member>
|
||||
<member name="M:FreeSql.DbSet`1.AddOrUpdateNavigateList(`0,System.Boolean,System.String)">
|
||||
<summary>
|
||||
联级保存导航集合
|
||||
</summary>
|
||||
<param name="item">实体对象</param>
|
||||
<param name="isAdd">是否为新增的实体对象</param>
|
||||
<param name="propertyName">指定保存的属性</param>
|
||||
</member>
|
||||
<member name="M:FreeSql.DbSet`1.Update(`0)">
|
||||
<summary>
|
||||
更新
|
||||
@ -240,9 +233,13 @@
|
||||
</summary>
|
||||
<param name="data"></param>
|
||||
</member>
|
||||
<member name="M:FreeSql.IBasicRepository`1.SaveManyToMany(`0,System.String)">
|
||||
<member name="M:FreeSql.IBasicRepository`1.SaveMany(`0,System.String)">
|
||||
<summary>
|
||||
保存实体的指定 ManyToMany 导航属性
|
||||
保存实体的指定 ManyToMany/OneToMany 导航属性(完整对比)<para></para>
|
||||
场景:在关闭级联保存功能之后,手工使用本方法<para></para>
|
||||
例子:保存商品的 OneToMany 集合属性,SaveMany(goods, "Skus")<para></para>
|
||||
当 goods.Skus 为空(非null)时,会删除表中已存在的所有数据<para></para>
|
||||
当 goods.Skus 不为空(非null)时,添加/更新后,删除表中不存在 Skus 集合属性的所有记录
|
||||
</summary>
|
||||
<param name="entity">实体对象</param>
|
||||
<param name="propertyName">属性名</param>
|
||||
|
@ -130,9 +130,9 @@ namespace FreeSql
|
||||
return entity;
|
||||
}
|
||||
|
||||
public void SaveManyToMany(TEntity entity, string propertyName)
|
||||
public void SaveMany(TEntity entity, string propertyName)
|
||||
{
|
||||
_dbset.SaveManyToMany(entity, propertyName);
|
||||
_dbset.SaveMany(entity, propertyName);
|
||||
_db.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
@ -64,9 +64,9 @@ namespace FreeSql
|
||||
return entity;
|
||||
}
|
||||
|
||||
async public Task SaveManyToManyAsync(TEntity entity, string propertyName)
|
||||
async public Task SaveManyAsync(TEntity entity, string propertyName)
|
||||
{
|
||||
await _dbset.SaveManyToManyAsync(entity, propertyName);
|
||||
await _dbset.SaveManyAsync(entity, propertyName);
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
@ -30,11 +30,15 @@ namespace FreeSql
|
||||
|
||||
TEntity InsertOrUpdate(TEntity entity);
|
||||
/// <summary>
|
||||
/// 保存实体的指定 ManyToMany 导航属性
|
||||
/// 保存实体的指定 ManyToMany/OneToMany 导航属性(完整对比)<para></para>
|
||||
/// 场景:在关闭级联保存功能之后,手工使用本方法<para></para>
|
||||
/// 例子:保存商品的 OneToMany 集合属性,SaveMany(goods, "Skus")<para></para>
|
||||
/// 当 goods.Skus 为空(非null)时,会删除表中已存在的所有数据<para></para>
|
||||
/// 当 goods.Skus 不为空(非null)时,添加/更新后,删除表中不存在 Skus 集合属性的所有记录
|
||||
/// </summary>
|
||||
/// <param name="entity">实体对象</param>
|
||||
/// <param name="propertyName">属性名</param>
|
||||
void SaveManyToMany(TEntity entity, string propertyName);
|
||||
void SaveMany(TEntity entity, string propertyName);
|
||||
|
||||
IUpdate<TEntity> UpdateDiy { get; }
|
||||
|
||||
@ -49,7 +53,7 @@ namespace FreeSql
|
||||
Task<int> UpdateAsync(TEntity entity);
|
||||
Task<int> UpdateAsync(IEnumerable<TEntity> entitys);
|
||||
Task<TEntity> InsertOrUpdateAsync(TEntity entity);
|
||||
Task SaveManyToManyAsync(TEntity entity, string propertyName);
|
||||
Task SaveManyAsync(TEntity entity, string propertyName);
|
||||
|
||||
Task<int> DeleteAsync(TEntity entity);
|
||||
Task<int> DeleteAsync(IEnumerable<TEntity> entitys);
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
### v0.6.5
|
||||
|
||||
- 修复 Repository 联级保存的 bug;
|
||||
- 修复 Repository 级联保存的 bug;
|
||||
- 添加工作单元开启方法;
|
||||
- 适配 .net framework 4.5、netstandard 2.0;
|
||||
|
||||
@ -192,7 +192,7 @@ fsql.GetGuidRepository<User>().Select.FromRepository(logRepository)
|
||||
|
||||
- 修复 AddOrUpdate/InsertOrUpdate 当主键无值时,仍然查询了一次数据库;
|
||||
- 增加 查询数据时 TrackToList 对导航集合的状态跟踪;
|
||||
- 完善 AddOrUpdateNavigateList 联级保存,忽略标记 IsIgnore 的集合属性;
|
||||
- 完善 AddOrUpdateNavigateList 级联保存,忽略标记 IsIgnore 的集合属性;
|
||||
- 完成 IFreeSql.Include、IncludeMany 功能;
|
||||
|
||||
### v0.5.12
|
||||
|
Reference in New Issue
Block a user