- 增加 DbContext、Repository SaveManyToMany 方法,实现手工保存 ManyToMany 关联数据;

This commit is contained in:
28810 2019-11-16 01:47:04 +08:00
parent 0f7dd75e64
commit e26dbfe526
8 changed files with 129 additions and 39 deletions

View File

@ -136,6 +136,13 @@ namespace FreeSql
/// <param name="data"></param> /// <param name="data"></param>
public void AddOrUpdate<TEntity>(TEntity data) where TEntity : class => this.Set<TEntity>().AddOrUpdate(data); public void AddOrUpdate<TEntity>(TEntity data) where TEntity : class => this.Set<TEntity>().AddOrUpdate(data);
/// <summary>
/// 保存实体的指定 ManyToMany 导航属性
/// </summary>
/// <param name="data">实体对象</param>
/// <param name="propertyName">属性名</param>
public void SaveManyToMany<TEntity>(TEntity data, string propertyName) where TEntity : class => this.Set<TEntity>().SaveManyToMany(data, propertyName);
/// <summary> /// <summary>
/// 附加实体,可用于不查询就更新或删除 /// 附加实体,可用于不查询就更新或删除
/// </summary> /// </summary>
@ -163,6 +170,7 @@ namespace FreeSql
public Task UpdateRangeAsync<TEntity>(IEnumerable<TEntity> data) where TEntity : class => this.Set<TEntity>().UpdateRangeAsync(data); public Task UpdateRangeAsync<TEntity>(IEnumerable<TEntity> data) where TEntity : class => this.Set<TEntity>().UpdateRangeAsync(data);
public Task AddOrUpdateAsync<TEntity>(TEntity data) where TEntity : class => this.Set<TEntity>().AddOrUpdateAsync(data); public Task AddOrUpdateAsync<TEntity>(TEntity data) where TEntity : class => this.Set<TEntity>().AddOrUpdateAsync(data);
public Task SaveManyToManyAsync<TEntity>(TEntity data, string propertyName) where TEntity : class => this.Set<TEntity>().SaveManyToManyAsync(data, propertyName);
#endif #endif
#endregion #endregion

View File

@ -132,44 +132,58 @@ namespace FreeSql
await AddOrUpdateNavigateListAsync(item, true); await AddOrUpdateNavigateListAsync(item, true);
} }
} }
async Task AddOrUpdateNavigateListAsync(TEntity item, bool isAdd)
async public Task SaveManyToManyAsync(TEntity item, string propertyName)
{
if (item == null) return;
if (_table.Properties.ContainsKey(propertyName) == 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();
var oldEnable = _db.Options.EnableAddOrUpdateNavigateList;
_db.Options.EnableAddOrUpdateNavigateList = false;
await AddOrUpdateNavigateListAsync(item, false, propertyName);
_db.Options.EnableAddOrUpdateNavigateList = oldEnable;
}
async Task AddOrUpdateNavigateListAsync(TEntity item, bool isAdd, string propertyName = null)
{ {
Type itemType = null; Type itemType = null;
foreach (var prop in _table.Properties) Func<PropertyInfo, Task> action = async prop =>
{ {
if (_table.ColumnsByCsIgnore.ContainsKey(prop.Key)) continue; if (_table.ColumnsByCsIgnore.ContainsKey(prop.Name)) return;
if (_table.ColumnsByCs.ContainsKey(prop.Key)) continue; if (_table.ColumnsByCs.ContainsKey(prop.Name)) return;
var tref = _table.GetTableRef(prop.Key, true); var tref = _table.GetTableRef(prop.Name, true);
if (tref == null) continue; if (tref == null) return;
switch (tref.RefType) switch (tref.RefType)
{ {
case Internal.Model.TableRefType.OneToOne: case Internal.Model.TableRefType.OneToOne:
case Internal.Model.TableRefType.ManyToOne: case Internal.Model.TableRefType.ManyToOne:
continue; return;
} }
object propVal = null; object propVal = null;
if (itemType == null) itemType = item.GetType(); if (itemType == null) itemType = item.GetType();
if (_table.TypeLazy != null && itemType == _table.TypeLazy) if (_table.TypeLazy != null && itemType == _table.TypeLazy)
{ {
var lazyField = _dicLazyIsSetField.GetOrAdd(_table.TypeLazy, tl => new ConcurrentDictionary<string, FieldInfo>()).GetOrAdd(prop.Key, propName => 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)); _table.TypeLazy.GetField($"__lazy__{propName}", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance));
if (lazyField != null) if (lazyField != null)
{ {
var lazyFieldValue = (bool)lazyField.GetValue(item); var lazyFieldValue = (bool)lazyField.GetValue(item);
if (lazyFieldValue == false) continue; if (lazyFieldValue == false) return;
} }
propVal = prop.Value.GetValue(item); propVal = prop.GetValue(item);
} }
else else
{ {
propVal = prop.Value.GetValue(item); propVal = prop.GetValue(item);
if (propVal == null) continue; if (propVal == null) return;
} }
var propValEach = propVal as IEnumerable; var propValEach = propVal as IEnumerable;
if (propValEach == null) continue; if (propValEach == null) return;
DbSet<object> refSet = GetDbSetObject(tref.RefEntityType); DbSet<object> refSet = GetDbSetObject(tref.RefEntityType);
switch (tref.RefType) switch (tref.RefType)
{ {
@ -276,7 +290,13 @@ namespace FreeSql
} }
break; break;
} }
} };
if (string.IsNullOrEmpty(propertyName))
foreach (var prop in _table.Properties)
await action(prop.Value);
else if (_table.Properties.TryGetValue(propertyName, out var prop))
await action(prop);
} }
#endregion #endregion

View File

@ -135,50 +135,70 @@ namespace FreeSql
AddOrUpdateNavigateList(item, true); AddOrUpdateNavigateList(item, true);
} }
} }
static ConcurrentDictionary<Type, ConcurrentDictionary<string, FieldInfo>> _dicLazyIsSetField = new ConcurrentDictionary<Type, ConcurrentDictionary<string, FieldInfo>>(); static ConcurrentDictionary<Type, ConcurrentDictionary<string, FieldInfo>> _dicLazyIsSetField = new ConcurrentDictionary<Type, ConcurrentDictionary<string, FieldInfo>>();
/// <summary> /// <summary>
/// 保存实体的指定 ManyToMany 导航属性
/// </summary>
/// <param name="item">实体对象</param>
/// <param name="propertyName">属性名</param>
public void SaveManyToMany(TEntity item, string propertyName)
{
if (item == null) return;
if (_table.Properties.ContainsKey(propertyName) == 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();
var oldEnable = _db.Options.EnableAddOrUpdateNavigateList;
_db.Options.EnableAddOrUpdateNavigateList = false;
AddOrUpdateNavigateList(item, false, propertyName);
_db.Options.EnableAddOrUpdateNavigateList = oldEnable;
}
/// <summary>
/// 联级保存导航集合 /// 联级保存导航集合
/// </summary> /// </summary>
/// <param name="item">实体对象</param> /// <param name="item">实体对象</param>
/// <param name="isAdd">是否为新增的实体对象</param> /// <param name="isAdd">是否为新增的实体对象</param>
void AddOrUpdateNavigateList(TEntity item, bool isAdd) /// <param name="propertyName">指定保存的属性</param>
void AddOrUpdateNavigateList(TEntity item, bool isAdd, string propertyName = null)
{ {
Type itemType = null; Type itemType = null;
foreach (var prop in _table.Properties) Action<PropertyInfo> action = prop =>
{ {
if (_table.ColumnsByCsIgnore.ContainsKey(prop.Key)) continue; if (_table.ColumnsByCsIgnore.ContainsKey(prop.Name)) return;
if (_table.ColumnsByCs.ContainsKey(prop.Key)) continue; if (_table.ColumnsByCs.ContainsKey(prop.Name)) return;
var tref = _table.GetTableRef(prop.Key, true); var tref = _table.GetTableRef(prop.Name, true);
if (tref == null) continue; if (tref == null) return;
switch (tref.RefType) switch (tref.RefType)
{ {
case Internal.Model.TableRefType.OneToOne: case Internal.Model.TableRefType.OneToOne:
case Internal.Model.TableRefType.ManyToOne: case Internal.Model.TableRefType.ManyToOne:
continue; return;
} }
object propVal = null; object propVal = null;
if (itemType == null) itemType = item.GetType(); if (itemType == null) itemType = item.GetType();
if (_table.TypeLazy != null && itemType == _table.TypeLazy) if (_table.TypeLazy != null && itemType == _table.TypeLazy)
{ {
var lazyField = _dicLazyIsSetField.GetOrAdd(_table.TypeLazy, tl => new ConcurrentDictionary<string, FieldInfo>()).GetOrAdd(prop.Key, propName => 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)); _table.TypeLazy.GetField($"__lazy__{propName}", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance));
if (lazyField != null) if (lazyField != null)
{ {
var lazyFieldValue = (bool)lazyField.GetValue(item); var lazyFieldValue = (bool)lazyField.GetValue(item);
if (lazyFieldValue == false) continue; if (lazyFieldValue == false) return;
} }
propVal = prop.Value.GetValue(item, null); propVal = prop.GetValue(item, null);
} }
else else
{ {
propVal = prop.Value.GetValue(item, null); propVal = prop.GetValue(item, null);
if (propVal == null) continue; if (propVal == null) return;
} }
var propValEach = propVal as IEnumerable; var propValEach = propVal as IEnumerable;
if (propValEach == null) continue; if (propValEach == null) return;
DbSet<object> refSet = GetDbSetObject(tref.RefEntityType); DbSet<object> refSet = GetDbSetObject(tref.RefEntityType);
switch (tref.RefType) switch (tref.RefType)
{ {
@ -230,7 +250,7 @@ namespace FreeSql
foreach (var midItem in midList) foreach (var midItem in midList)
{ {
var curContains = new List<int>(); var curContains = new List<int>();
for(var curIdx = 0; curIdx < curList.Count; curIdx ++) for (var curIdx = 0; curIdx < curList.Count; curIdx++)
{ {
var isEquals = true; var isEquals = true;
for (var midcolidx = tref.Columns.Count; midcolidx < tref.MiddleColumns.Count; midcolidx++) for (var midcolidx = tref.Columns.Count; midcolidx < tref.MiddleColumns.Count; midcolidx++)
@ -285,7 +305,13 @@ namespace FreeSql
} }
break; break;
} }
} };
if (string.IsNullOrEmpty(propertyName))
foreach (var prop in _table.Properties)
action(prop.Value);
else if (_table.Properties.TryGetValue(propertyName, out var prop))
action(prop);
} }
#endregion #endregion

View File

@ -32,6 +32,13 @@
<typeparam name="TEntity"></typeparam> <typeparam name="TEntity"></typeparam>
<param name="data"></param> <param name="data"></param>
</member> </member>
<member name="M:FreeSql.DbContext.SaveManyToMany``1(``0,System.String)">
<summary>
保存实体的指定 ManyToMany 导航属性
</summary>
<param name="data">实体对象</param>
<param name="propertyName">属性名</param>
</member>
<member name="M:FreeSql.DbContext.Attach``1(``0)"> <member name="M:FreeSql.DbContext.Attach``1(``0)">
<summary> <summary>
附加实体,可用于不查询就更新或删除 附加实体,可用于不查询就更新或删除
@ -99,25 +106,26 @@
清空状态数据 清空状态数据
</summary> </summary>
</member> </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)"> <member name="M:FreeSql.DbSet`1.Add(`0)">
<summary> <summary>
添加 添加
</summary> </summary>
<param name="data"></param> <param name="data"></param>
</member> </member>
<member name="M:FreeSql.DbSet`1.AddOrUpdateNavigateList(`0,System.Boolean)"> <member name="M:FreeSql.DbSet`1.SaveManyToMany(`0,System.String)">
<summary>
保存实体的指定 ManyToMany 导航属性
</summary>
<param name="item">实体对象</param>
<param name="propertyName">属性名</param>
</member>
<member name="M:FreeSql.DbSet`1.AddOrUpdateNavigateList(`0,System.Boolean,System.String)">
<summary> <summary>
联级保存导航集合 联级保存导航集合
</summary> </summary>
<param name="item">实体对象</param> <param name="item">实体对象</param>
<param name="isAdd">是否为新增的实体对象</param> <param name="isAdd">是否为新增的实体对象</param>
<param name="propertyName">指定保存的属性</param>
</member> </member>
<member name="M:FreeSql.DbSet`1.Update(`0)"> <member name="M:FreeSql.DbSet`1.Update(`0)">
<summary> <summary>
@ -225,6 +233,13 @@
</summary> </summary>
<param name="data"></param> <param name="data"></param>
</member> </member>
<member name="M:FreeSql.IBasicRepository`1.SaveManyToMany(`0,System.String)">
<summary>
保存实体的指定 ManyToMany 导航属性
</summary>
<param name="entity">实体对象</param>
<param name="propertyName">属性名</param>
</member>
<member name="P:FreeSql.IUnitOfWork.Enable"> <member name="P:FreeSql.IUnitOfWork.Enable">
<summary> <summary>
是否启用工作单元 是否启用工作单元

View File

@ -129,6 +129,12 @@ namespace FreeSql
_db.SaveChanges(); _db.SaveChanges();
return entity; return entity;
} }
public void SaveManyToMany(TEntity entity, string propertyName)
{
_dbset.SaveManyToMany(entity, propertyName);
_db.SaveChanges();
}
} }
public abstract partial class BaseRepository<TEntity, TKey> : BaseRepository<TEntity>, IBaseRepository<TEntity, TKey> public abstract partial class BaseRepository<TEntity, TKey> : BaseRepository<TEntity>, IBaseRepository<TEntity, TKey>

View File

@ -36,7 +36,7 @@ namespace FreeSql
async public virtual Task<TEntity> InsertAsync(TEntity entity) async public virtual Task<TEntity> InsertAsync(TEntity entity)
{ {
await _dbset.AddAsync(entity); await _dbset.AddAsync(entity);
_db.SaveChanges(); await _db.SaveChangesAsync();
return entity; return entity;
} }
async public virtual Task<List<TEntity>> InsertAsync(IEnumerable<TEntity> entitys) async public virtual Task<List<TEntity>> InsertAsync(IEnumerable<TEntity> entitys)
@ -60,9 +60,15 @@ namespace FreeSql
async public Task<TEntity> InsertOrUpdateAsync(TEntity entity) async public Task<TEntity> InsertOrUpdateAsync(TEntity entity)
{ {
await _dbset.AddOrUpdateAsync(entity); await _dbset.AddOrUpdateAsync(entity);
_db.SaveChanges(); await _db.SaveChangesAsync();
return entity; return entity;
} }
async public Task SaveManyToManyAsync(TEntity entity, string propertyName)
{
await _dbset.SaveManyToManyAsync(entity, propertyName);
await _db.SaveChangesAsync();
}
} }
partial class BaseRepository<TEntity, TKey> partial class BaseRepository<TEntity, TKey>

View File

@ -29,6 +29,12 @@ namespace FreeSql
int Update(IEnumerable<TEntity> entitys); int Update(IEnumerable<TEntity> entitys);
TEntity InsertOrUpdate(TEntity entity); TEntity InsertOrUpdate(TEntity entity);
/// <summary>
/// 保存实体的指定 ManyToMany 导航属性
/// </summary>
/// <param name="entity">实体对象</param>
/// <param name="propertyName">属性名</param>
void SaveManyToMany(TEntity entity, string propertyName);
IUpdate<TEntity> UpdateDiy { get; } IUpdate<TEntity> UpdateDiy { get; }
@ -43,6 +49,7 @@ namespace FreeSql
Task<int> UpdateAsync(TEntity entity); Task<int> UpdateAsync(TEntity entity);
Task<int> UpdateAsync(IEnumerable<TEntity> entitys); Task<int> UpdateAsync(IEnumerable<TEntity> entitys);
Task<TEntity> InsertOrUpdateAsync(TEntity entity); Task<TEntity> InsertOrUpdateAsync(TEntity entity);
Task SaveManyToManyAsync(TEntity entity, string propertyName);
Task<int> DeleteAsync(TEntity entity); Task<int> DeleteAsync(TEntity entity);
Task<int> DeleteAsync(IEnumerable<TEntity> entitys); Task<int> DeleteAsync(IEnumerable<TEntity> entitys);

View File

@ -415,7 +415,9 @@ namespace FreeSql.Tests
} }
}; };
var repo = g.sqlite.GetRepository<Song>(); var repo = g.sqlite.GetRepository<Song>();
//repo.DbContextOptions.EnableAddOrUpdateNavigateList = false; //关闭联级保存功能
repo.Insert(ss); repo.Insert(ss);
repo.SaveManyToMany(ss[0], "Tags"); //指定保存 Tags 多对多属性
ss[0].Name = "爱你一万年.mp5"; ss[0].Name = "爱你一万年.mp5";
ss[0].Tags.Clear(); ss[0].Tags.Clear();