- 调整 DbSet/Repository DeleteCascadeByDatabase/Delete 区别于数据库/内存的级联删除;#609

This commit is contained in:
2881099
2022-05-04 22:38:17 +08:00
parent a80d2cdf9d
commit 0ecab32f34
40 changed files with 420 additions and 355 deletions

View File

@ -46,7 +46,7 @@ namespace FreeSql
_optionsPriv = new DbContextOptions();
if (FreeSqlDbContextExtensions._dicSetDbContextOptions.TryGetValue(OrmOriginal.Ado.Identifier, out var opt))
{
_optionsPriv.EnableAddOrUpdateNavigate = opt.EnableAddOrUpdateNavigate;
_optionsPriv.EnableCascadeSave = opt.EnableCascadeSave;
_optionsPriv.EnableGlobalFilter = opt.EnableGlobalFilter;
_optionsPriv.NoneParameter = opt.NoneParameter;
_optionsPriv.OnEntityChange = opt.OnEntityChange;
@ -225,29 +225,29 @@ namespace FreeSql
}
#if net40
#else
public Task AddAsync<TEntity>(TEntity data) where TEntity : class
public Task AddAsync<TEntity>(TEntity data, CancellationToken cancellationToken = default) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
return this.Set<TEntity>().AddAsync(data);
return this.Set<TEntity>().AddAsync(data, cancellationToken);
}
public Task AddRangeAsync<TEntity>(IEnumerable<TEntity> data) where TEntity : class => this.Set<TEntity>().AddRangeAsync(data);
public Task AddRangeAsync<TEntity>(IEnumerable<TEntity> data, CancellationToken cancellationToken = default) where TEntity : class => this.Set<TEntity>().AddRangeAsync(data, cancellationToken);
public Task UpdateAsync<TEntity>(TEntity data) where TEntity : class
public Task UpdateAsync<TEntity>(TEntity data, CancellationToken cancellationToken = default) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
return this.Set<TEntity>().UpdateAsync(data);
return this.Set<TEntity>().UpdateAsync(data, cancellationToken);
}
public Task UpdateRangeAsync<TEntity>(IEnumerable<TEntity> data) where TEntity : class => this.Set<TEntity>().UpdateRangeAsync(data);
public Task UpdateRangeAsync<TEntity>(IEnumerable<TEntity> data, CancellationToken cancellationToken = default) where TEntity : class => this.Set<TEntity>().UpdateRangeAsync(data, cancellationToken);
public Task AddOrUpdateAsync<TEntity>(TEntity data) where TEntity : class
public Task AddOrUpdateAsync<TEntity>(TEntity data, CancellationToken cancellationToken = default) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
return this.Set<TEntity>().AddOrUpdateAsync(data);
return this.Set<TEntity>().AddOrUpdateAsync(data, cancellationToken);
}
public Task SaveManyAsync<TEntity>(TEntity data, string propertyName) where TEntity : class
public Task SaveManyAsync<TEntity>(TEntity data, string propertyName, CancellationToken cancellationToken = default) where TEntity : class
{
CheckEntityTypeOrThrow(typeof(TEntity));
return this.Set<TEntity>().SaveManyAsync(data, propertyName);
return this.Set<TEntity>().SaveManyAsync(data, propertyName, cancellationToken);
}
#endif
#endregion

View File

@ -21,16 +21,16 @@ namespace FreeSql
/// - 属性集合为空时(!=null),删除他们的所有关联数据(中间表)<para></para>
/// - 属性集合不为空时,与数据库存在的关联数据(中间表)完全对比,计算出应该删除和添加的记录
/// </summary>
public bool EnableAddOrUpdateNavigate { get; set; } = false;
public bool EnableCascadeSave { get; set; } = false;
/// <summary>
/// 因增加支持 OneToOne 级联保存,请了解机制,已改名为 EnableAddOrUpdateNavigate
/// 因增加支持 OneToOne 级联保存,和基于内存的级联删除,已改名为 EnableCascadeSave
/// </summary>
[Obsolete("因增加支持 OneToOne 级联保存,请了解机制,已改名为 EnableAddOrUpdateNavigate")]
[Obsolete("因增加支持 OneToOne 级联保存,和基于内存的级联删除,已改名为 EnableCascadeSave")]
public bool EnableAddOrUpdateNavigateList
{
get => EnableAddOrUpdateNavigate;
set => EnableAddOrUpdateNavigate = value;
get => EnableCascadeSave;
set => EnableCascadeSave = value;
}
/// <summary>

View File

@ -166,6 +166,15 @@ namespace FreeSql
{
ds.AttachRange(_states.Values.OrderBy(a => a.Time).Select(a => a.Value).ToArray());
}
void StatesRemoveByObjects(IEnumerable<object> data)
{
if (data == null) return;
foreach (var item in data)
{
var stateKey = _db.OrmOriginal.GetEntityKeyString(_entityType, item, false);
_states.TryRemove(stateKey, out var trystate);
}
}
public class EntityState
{

View File

@ -55,7 +55,7 @@ namespace FreeSql
_db.OrmOriginal.SetEntityValueWithPropertyName(_entityType, data, _tableIdentitys[0].CsName, idtval);
_db._entityChangeReport.Add(new DbContext.EntityChangeReport.ChangeInfo { Object = data, Type = DbContext.EntityChangeType.Insert });
Attach(data);
if (_db.Options.EnableAddOrUpdateNavigate)
if (_db.Options.EnableCascadeSave)
await AddOrUpdateNavigateAsync(data, true, null, cancellationToken);
}
else
@ -66,7 +66,7 @@ namespace FreeSql
IncrAffrows(1);
_db.OrmOriginal.MapEntityValue(_entityType, newval, data);
Attach(newval);
if (_db.Options.EnableAddOrUpdateNavigate)
if (_db.Options.EnableCascadeSave)
await AddOrUpdateNavigateAsync(data, true, null, cancellationToken);
}
return;
@ -79,7 +79,7 @@ namespace FreeSql
_db.OrmOriginal.SetEntityValueWithPropertyName(_entityType, data, _tableIdentitys[0].CsName, idtval);
_db._entityChangeReport.Add(new DbContext.EntityChangeReport.ChangeInfo { Object = data, Type = DbContext.EntityChangeType.Insert });
Attach(data);
if (_db.Options.EnableAddOrUpdateNavigate)
if (_db.Options.EnableCascadeSave)
await AddOrUpdateNavigateAsync(data, true, null, cancellationToken);
return;
}
@ -88,7 +88,7 @@ namespace FreeSql
}
EnqueueToDbContext(DbContext.EntityChangeType.Insert, CreateEntityState(data));
Attach(data);
if (_db.Options.EnableAddOrUpdateNavigate)
if (_db.Options.EnableCascadeSave)
await AddOrUpdateNavigateAsync(data, true, null, cancellationToken);
}
public Task AddAsync(TEntity data, CancellationToken cancellationToken = default) => AddPrivAsync(data, true, cancellationToken);
@ -121,7 +121,7 @@ namespace FreeSql
_db.OrmOriginal.MapEntityValue(_entityType, rets[idx++], s);
IncrAffrows(rets.Count);
AttachRange(rets);
if (_db.Options.EnableAddOrUpdateNavigate)
if (_db.Options.EnableCascadeSave)
foreach (var item in data)
await AddOrUpdateNavigateAsync(item, true, null, cancellationToken);
return;
@ -139,7 +139,7 @@ namespace FreeSql
foreach (var item in data)
EnqueueToDbContext(DbContext.EntityChangeType.Insert, CreateEntityState(item));
AttachRange(data);
if (_db.Options.EnableAddOrUpdateNavigate)
if (_db.Options.EnableCascadeSave)
foreach (var item in data)
await AddOrUpdateNavigateAsync(item, true, null, cancellationToken);
}
@ -161,8 +161,8 @@ namespace FreeSql
}
await DbContextFlushCommandAsync(cancellationToken);
var oldEnable = _db.Options.EnableAddOrUpdateNavigate;
_db.Options.EnableAddOrUpdateNavigate = false;
var oldEnable = _db.Options.EnableCascadeSave;
_db.Options.EnableCascadeSave = false;
try
{
await AddOrUpdateNavigateAsync(item, false, propertyName, cancellationToken);
@ -198,7 +198,7 @@ namespace FreeSql
}
finally
{
_db.Options.EnableAddOrUpdateNavigate = oldEnable;
_db.Options.EnableCascadeSave = oldEnable;
}
}
async Task AddOrUpdateNavigateAsync(TEntity item, bool isAdd, string propertyName, CancellationToken cancellationToken)
@ -447,7 +447,7 @@ namespace FreeSql
state.OldValue = item;
EnqueueToDbContext(DbContext.EntityChangeType.Update, state);
}
if (_db.Options.EnableAddOrUpdateNavigate)
if (_db.Options.EnableCascadeSave)
foreach (var item in data)
await AddOrUpdateNavigateAsync(item, false, null, cancellationToken);
}
@ -499,155 +499,11 @@ namespace FreeSql
#endregion
#region RemoveCascadeAsync
public Task<List<object>> RemoveCascadeAsync(TEntity data, CancellationToken cancellationToken = default) => RemoveRangeCascadeAsync(new[] { data }, cancellationToken);
public Task<List<object>> RemoveCascadeAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => RemoveRangeCascadeAsync(Select.Where(predicate).ToList(), cancellationToken);
async public Task<List<object>> RemoveRangeCascadeAsync(IEnumerable<TEntity> data, CancellationToken cancellationToken = default)
public Task<List<object>> RemoveCascadeByDatabaseAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => RemoveRangeCascadeByMemoryOrDatabaseAsync(Select.Where(predicate).ToList(), false, cancellationToken);
async internal protected Task<List<object>> RemoveRangeCascadeByMemoryOrDatabaseAsync(IEnumerable<TEntity> data, bool inMemory, CancellationToken cancellationToken = default)
{
var returnDeleted = new List<object>();
if (data?.Any() != true) return returnDeleted;
await DbContextFlushCommandAsync(cancellationToken);
var fsql = _db.Orm;
if (LocalGetNavigates(_table).Any() == false)
{
if (CanRemove(data, true) == false) return returnDeleted;
foreach (var item in data) //防止清除 Identity/Guid
{
var state = CreateEntityState(item);
_states.TryRemove(state.Key, out var trystate);
EnqueueToDbContext(DbContext.EntityChangeType.Delete, state);
}
await DbContextFlushCommandAsync(cancellationToken);
returnDeleted.AddRange(data.Select(a => (object)a));
return returnDeleted;
}
var commonUtils = (fsql.Select<object>() as Internal.CommonProvider.Select0Provider)._commonUtils;
var eachdic = new Dictionary<string, bool>();
var rootItems = data.Select(a => (object)a).ToArray();
var rootDbSet = _db.Set<object>();
rootDbSet.AsType(_table.Type);
rootDbSet.AttachRange(rootItems);
await LocalEachAsync(rootDbSet, rootItems, true);
return returnDeleted;
List<NativeTuple<TableRef, PropertyInfo>> LocalGetNavigates(TableInfo tb)
{
return tb.Properties.Where(a => tb.ColumnsByCs.ContainsKey(a.Key) == false)
.Select(a => new NativeTuple<TableRef, PropertyInfo>(tb.GetTableRef(a.Key, false), a.Value))
.Where(a => a.Item1 != null && a.Item1.RefType != TableRefType.ManyToOne)
.ToList();
}
async Task LocalEachAsync(DbSet<object> dbset, IEnumerable<object> items, bool isOneToOne)
{
items = items?.Where(item =>
{
var itemkeyStr = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetEntityKeyString(fsql, dbset.EntityType, item, false);
var eachdicKey = $"{dbset.EntityType.FullName},{itemkeyStr}";
if (eachdic.ContainsKey(eachdicKey)) return false;
eachdic.Add(eachdicKey, true);
return true;
}).ToList();
if (items?.Any() != true) return;
var tb = fsql.CodeFirst.GetTableByEntity(dbset.EntityType);
var navs = LocalGetNavigates(tb);
var otos = navs.Where(a => a.Item1.RefType == TableRefType.OneToOne).ToList();
if (isOneToOne && otos.Any())
{
foreach (var oto in otos)
{
var childTable = fsql.CodeFirst.GetTableByEntity(oto.Item1.RefEntityType);
var childDbSet = _db.Set<object>();
childDbSet.AsType(oto.Item1.RefEntityType);
var refitems = items.Select(item =>
{
var refitem = oto.Item1.RefEntityType.CreateInstanceGetDefaultValue();
for (var a = 0; a < oto.Item1.Columns.Count; a++)
{
var colval = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, oto.Item1.Columns[a].CsName);
FreeSql.Extensions.EntityUtil.EntityUtilExtensions.SetPropertyValue(childTable, refitem, oto.Item1.RefColumns[a].CsName, colval);
}
return refitem;
}).ToList();
var childs = await childDbSet.Select.Where(commonUtils.WhereItems(oto.Item1.RefColumns.ToArray(), "a.", refitems)).ToListAsync(false, cancellationToken);
await LocalEachAsync(childDbSet, childs, false);
}
}
var otms = navs.Where(a => a.Item1.RefType == TableRefType.OneToMany).ToList();
if (otms.Any())
{
foreach (var otm in otms)
{
var childTable = fsql.CodeFirst.GetTableByEntity(otm.Item1.RefEntityType);
var childDbSet = _db.Set<object>();
childDbSet.AsType(otm.Item1.RefEntityType);
var refitems = items.Select(item =>
{
var refitem = otm.Item1.RefEntityType.CreateInstanceGetDefaultValue();
for (var a = 0; a < otm.Item1.Columns.Count; a++)
{
var colval = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, otm.Item1.Columns[a].CsName);
FreeSql.Extensions.EntityUtil.EntityUtilExtensions.SetPropertyValue(childTable, refitem, otm.Item1.RefColumns[a].CsName, colval);
}
return refitem;
}).ToList();
var childs = await childDbSet.Select.Where(commonUtils.WhereItems(otm.Item1.RefColumns.ToArray(), "a.", refitems)).ToListAsync(false, cancellationToken);
await LocalEachAsync(childDbSet, childs, true);
}
}
var mtms = navs.Where(a => a.Item1.RefType == TableRefType.ManyToMany).ToList();
if (mtms.Any())
{
foreach (var mtm in mtms)
{
var childTable = fsql.CodeFirst.GetTableByEntity(mtm.Item1.RefMiddleEntityType);
var childDbSet = _db.Set<object>();
childDbSet.AsType(mtm.Item1.RefMiddleEntityType);
var miditems = items.Select(item =>
{
var refitem = mtm.Item1.RefMiddleEntityType.CreateInstanceGetDefaultValue();
for (var a = 0; a < mtm.Item1.Columns.Count; a++)
{
var colval = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, mtm.Item1.Columns[a].CsName);
FreeSql.Extensions.EntityUtil.EntityUtilExtensions.SetPropertyValue(childTable, refitem, mtm.Item1.MiddleColumns[a].CsName, colval);
}
return refitem;
}).ToList();
var childs = await childDbSet.Select.Where(commonUtils.WhereItems(mtm.Item1.MiddleColumns.Take(mtm.Item1.Columns.Count).ToArray(), "a.", miditems)).ToListAsync(false, cancellationToken);
await LocalEachAsync(childDbSet, childs, true);
}
}
if (dbset == rootDbSet)
{
if (CanRemove(data, true) == false) return;
foreach (var item in data) //防止清除 Identity/Guid
{
var state = CreateEntityState(item);
_states.TryRemove(state.Key, out var trystate);
EnqueueToDbContext(DbContext.EntityChangeType.Delete, state);
}
await DbContextFlushCommandAsync(cancellationToken);
}
else
{
if (dbset.CanRemove(items, true) == false) return;
foreach (var item in items) //防止清除 Identity/Guid
{
var state = dbset.CreateEntityState(item);
dbset._states.TryRemove(state.Key, out var trystate);
dbset.EnqueueToDbContext(DbContext.EntityChangeType.Delete, state);
}
await DbContextFlushCommandAsync(cancellationToken);
}
returnDeleted.AddRange(items);
}
//临时调用同步方法,后续会改
return await Task.FromResult(RemoveRangeCascadeByMemoryOrDatabase(data, inMemory));
}
#endregion
}

View File

@ -53,7 +53,7 @@ namespace FreeSql
_db.OrmOriginal.SetEntityValueWithPropertyName(_entityType, data, _tableIdentitys[0].CsName, idtval);
_db._entityChangeReport.Add(new DbContext.EntityChangeReport.ChangeInfo { Object = data, Type = DbContext.EntityChangeType.Insert });
Attach(data);
if (_db.Options.EnableAddOrUpdateNavigate)
if (_db.Options.EnableCascadeSave)
AddOrUpdateNavigate(data, true, null);
}
else
@ -64,7 +64,7 @@ namespace FreeSql
IncrAffrows(1);
_db.OrmOriginal.MapEntityValue(_entityType, newval, data);
Attach(newval);
if (_db.Options.EnableAddOrUpdateNavigate)
if (_db.Options.EnableCascadeSave)
AddOrUpdateNavigate(data, true, null);
}
return;
@ -77,7 +77,7 @@ namespace FreeSql
_db.OrmOriginal.SetEntityValueWithPropertyName(_entityType, data, _tableIdentitys[0].CsName, idtval);
_db._entityChangeReport.Add(new DbContext.EntityChangeReport.ChangeInfo { Object = data, Type = DbContext.EntityChangeType.Insert });
Attach(data);
if (_db.Options.EnableAddOrUpdateNavigate)
if (_db.Options.EnableCascadeSave)
AddOrUpdateNavigate(data, true, null);
return;
}
@ -86,7 +86,7 @@ namespace FreeSql
}
EnqueueToDbContext(DbContext.EntityChangeType.Insert, CreateEntityState(data));
Attach(data);
if (_db.Options.EnableAddOrUpdateNavigate)
if (_db.Options.EnableCascadeSave)
AddOrUpdateNavigate(data, true, null);
}
/// <summary>
@ -123,7 +123,7 @@ namespace FreeSql
_db.OrmOriginal.MapEntityValue(_entityType, rets[idx++], s);
IncrAffrows(rets.Count);
AttachRange(rets);
if (_db.Options.EnableAddOrUpdateNavigate)
if (_db.Options.EnableCascadeSave)
foreach (var item in data)
AddOrUpdateNavigate(item, true, null);
return;
@ -141,7 +141,7 @@ namespace FreeSql
foreach (var item in data)
EnqueueToDbContext(DbContext.EntityChangeType.Insert, CreateEntityState(item));
AttachRange(data);
if (_db.Options.EnableAddOrUpdateNavigate)
if (_db.Options.EnableCascadeSave)
foreach (var item in data)
AddOrUpdateNavigate(item, true, null);
}
@ -172,8 +172,8 @@ namespace FreeSql
}
DbContextFlushCommand();
var oldEnable = _db.Options.EnableAddOrUpdateNavigate;
_db.Options.EnableAddOrUpdateNavigate = false;
var oldEnable = _db.Options.EnableCascadeSave;
_db.Options.EnableCascadeSave = false;
try
{
AddOrUpdateNavigate(item, false, propertyName);
@ -209,7 +209,7 @@ namespace FreeSql
}
finally
{
_db.Options.EnableAddOrUpdateNavigate = oldEnable;
_db.Options.EnableCascadeSave = oldEnable;
}
}
void AddOrUpdateNavigate(TEntity item, bool isAdd, string propertyName)
@ -488,7 +488,7 @@ namespace FreeSql
state.OldValue = item;
EnqueueToDbContext(DbContext.EntityChangeType.Update, state);
}
if (_db.Options.EnableAddOrUpdateNavigate)
if (_db.Options.EnableCascadeSave)
foreach (var item in data)
AddOrUpdateNavigate(item, false, null);
}
@ -510,6 +510,11 @@ namespace FreeSql
public void Remove(TEntity data) => RemoveRange(new[] { data });
public void RemoveRange(IEnumerable<TEntity> data)
{
if (_db.Options.EnableCascadeSave)
{
RemoveRangeCascadeByMemoryOrDatabase(data, true);
return;
}
if (CanRemove(data, true) == false) return;
foreach (var item in data)
{
@ -608,8 +613,8 @@ namespace FreeSql
if (data == null) data = _dataEditing;
var beforeAffrows = 0;
if (data == null) return 0;
var oldEnable = _db.Options.EnableAddOrUpdateNavigate;
_db.Options.EnableAddOrUpdateNavigate = false;
var oldEnable = _db.Options.EnableCascadeSave;
_db.Options.EnableCascadeSave = false;
try
{
DbContextFlushCommand();
@ -651,7 +656,7 @@ namespace FreeSql
{
_dataEditing = null;
_statesEditing.Clear();
_db.Options.EnableAddOrUpdateNavigate = oldEnable;
_db.Options.EnableCascadeSave = oldEnable;
}
return _db._affrows - beforeAffrows;
}
@ -659,33 +664,31 @@ namespace FreeSql
#region RemoveCascade
/// <summary>
/// 根据设置的导航属性,递归查询删除 OneToOne/OneToMany/ManyToMany 数据,并返回已删除的数据
/// 根据设置的 OneToOne/OneToMany/ManyToMany 导航属性,级联查询所有的数据库记录,删除并返回它们
/// </summary>
/// <param name="data"></param>
/// <param name="predicate"></param>
/// <returns></returns>
public List<object> RemoveCascade(TEntity data) => RemoveRangeCascade(new[] { data });
public List<object> RemoveCascade(Expression<Func<TEntity, bool>> predicate) => RemoveRangeCascade(Select.Where(predicate).ToList());
public List<object> RemoveRangeCascade(IEnumerable<TEntity> data)
public List<object> RemoveCascadeByDatabase(Expression<Func<TEntity, bool>> predicate) => RemoveRangeCascadeByMemoryOrDatabase(Select.Where(predicate).ToList(), false);
internal protected List<object> RemoveRangeCascadeByMemoryOrDatabase(IEnumerable<TEntity> data, bool inMemory)
{
var returnDeleted = new List<object>();
var returnDeleted = inMemory ? null : new List<object>();
if (data?.Any() != true) return returnDeleted;
DbContextFlushCommand();
var fsql = _db.Orm;
if (LocalGetNavigates(_table).Any() == false)
{
if (CanRemove(data, true) == false) return returnDeleted;
foreach (var item in data) //防止清除 Identity/Guid
foreach (var item in data) //不直接调用 Remove防止清除 Identity/Guid
{
var state = CreateEntityState(item);
_states.TryRemove(state.Key, out var trystate);
if (inMemory) _db.OrmOriginal.ClearEntityPrimaryValueWithIdentityAndGuid(_entityType, item);
EnqueueToDbContext(DbContext.EntityChangeType.Delete, state);
}
DbContextFlushCommand();
returnDeleted.AddRange(data.Select(a => (object)a));
returnDeleted?.AddRange(data.Select(a => (object)a));
return returnDeleted;
}
var fsql = _db.Orm;
var commonUtils = (fsql.Select<object>() as Internal.CommonProvider.Select0Provider)._commonUtils;
var eachdic = new Dictionary<string, bool>();
var rootItems = data.Select(a => (object)a).ToArray();
@ -693,6 +696,7 @@ namespace FreeSql
rootDbSet.AsType(_table.Type);
rootDbSet.AttachRange(rootItems);
LocalEach(rootDbSet, rootItems, true);
rootDbSet.FlushState();
return returnDeleted;
List<NativeTuple<TableRef, PropertyInfo>> LocalGetNavigates(TableInfo tb)
@ -722,21 +726,31 @@ namespace FreeSql
{
foreach (var oto in otos)
{
var childTable = fsql.CodeFirst.GetTableByEntity(oto.Item1.RefEntityType);
var childDbSet = _db.Set<object>();
childDbSet.AsType(oto.Item1.RefEntityType);
var refitems = items.Select(item =>
var refset = _db.Set<object>();
refset.AsType(oto.Item1.RefEntityType);
if (inMemory)
{
var refitem = oto.Item1.RefEntityType.CreateInstanceGetDefaultValue();
for (var a = 0; a < oto.Item1.Columns.Count; a++)
var refitems = items.Select(item => FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, oto.Item2.Name)).Where(item => item != null).ToList();
refset.AttachRange(refitems);
LocalEach(refset, refitems, false);
}
else
{
var reftb = fsql.CodeFirst.GetTableByEntity(oto.Item1.RefEntityType);
var refwhereItems = items.Select(item =>
{
var colval = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, oto.Item1.Columns[a].CsName);
FreeSql.Extensions.EntityUtil.EntityUtilExtensions.SetPropertyValue(childTable, refitem, oto.Item1.RefColumns[a].CsName, colval);
}
return refitem;
}).ToList();
var childs = childDbSet.Select.Where(commonUtils.WhereItems(oto.Item1.RefColumns.ToArray(), "a.", refitems)).ToList();
LocalEach(childDbSet, childs, false);
var refitem = oto.Item1.RefEntityType.CreateInstanceGetDefaultValue();
for (var a = 0; a < oto.Item1.Columns.Count; a++)
{
var colval = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, oto.Item1.Columns[a].CsName);
FreeSql.Extensions.EntityUtil.EntityUtilExtensions.SetPropertyValue(reftb, refitem, oto.Item1.RefColumns[a].CsName, colval);
}
return refitem;
}).ToList();
var refitems = refset.Select.Where(commonUtils.WhereItems(oto.Item1.RefColumns.ToArray(), "a.", refwhereItems)).ToList();
LocalEach(refset, refitems, false);
}
}
}
@ -745,21 +759,38 @@ namespace FreeSql
{
foreach (var otm in otms)
{
var childTable = fsql.CodeFirst.GetTableByEntity(otm.Item1.RefEntityType);
var childDbSet = _db.Set<object>();
childDbSet.AsType(otm.Item1.RefEntityType);
var refitems = items.Select(item =>
var refset = _db.Set<object>();
refset.AsType(otm.Item1.RefEntityType);
if (inMemory)
{
var refitem = otm.Item1.RefEntityType.CreateInstanceGetDefaultValue();
for (var a = 0; a < otm.Item1.Columns.Count; a++)
var refitems = items.Select(item =>
{
var colval = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, otm.Item1.Columns[a].CsName);
FreeSql.Extensions.EntityUtil.EntityUtilExtensions.SetPropertyValue(childTable, refitem, otm.Item1.RefColumns[a].CsName, colval);
}
return refitem;
}).ToList();
var childs = childDbSet.Select.Where(commonUtils.WhereItems(otm.Item1.RefColumns.ToArray(), "a.", refitems)).ToList();
LocalEach(childDbSet, childs, true);
var reflist = new List<object>();
var reflistie = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, otm.Item2.Name) as IEnumerable;
if (reflistie == null) return null;
foreach (var refitem in reflistie) reflist.Add(refitem);
return reflist;
}).Where(itemlst => itemlst != null).SelectMany(itemlst => itemlst).ToList();
refset.AttachRange(refitems);
LocalEach(refset, refitems, true);
}
else
{
var reftb = fsql.CodeFirst.GetTableByEntity(otm.Item1.RefEntityType);
var refwhereItems = items.Select(item =>
{
var refitem = otm.Item1.RefEntityType.CreateInstanceGetDefaultValue();
for (var a = 0; a < otm.Item1.Columns.Count; a++)
{
var colval = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, otm.Item1.Columns[a].CsName);
FreeSql.Extensions.EntityUtil.EntityUtilExtensions.SetPropertyValue(reftb, refitem, otm.Item1.RefColumns[a].CsName, colval);
}
return refitem;
}).ToList();
var childs = refset.Select.Where(commonUtils.WhereItems(otm.Item1.RefColumns.ToArray(), "a.", refwhereItems)).ToList();
LocalEach(refset, childs, true);
}
}
}
@ -768,49 +799,86 @@ namespace FreeSql
{
foreach (var mtm in mtms)
{
var midset = _db.Set<object>();
midset.AsType(mtm.Item1.RefMiddleEntityType);
var childTable = fsql.CodeFirst.GetTableByEntity(mtm.Item1.RefMiddleEntityType);
var childDbSet = _db.Set<object>();
childDbSet.AsType(mtm.Item1.RefMiddleEntityType);
var miditems = items.Select(item =>
if (inMemory)
{
var refitem = mtm.Item1.RefMiddleEntityType.CreateInstanceGetDefaultValue();
for (var a = 0; a < mtm.Item1.Columns.Count; a++)
var miditems = items.Select(item =>
{
var colval = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, mtm.Item1.Columns[a].CsName);
FreeSql.Extensions.EntityUtil.EntityUtilExtensions.SetPropertyValue(childTable, refitem, mtm.Item1.MiddleColumns[a].CsName, colval);
}
return refitem;
}).ToList();
var childs = childDbSet.Select.Where(commonUtils.WhereItems(mtm.Item1.MiddleColumns.Take(mtm.Item1.Columns.Count).ToArray(), "a.", miditems)).ToList();
LocalEach(childDbSet, childs, true);
var midlist = new List<object>();
var refitems = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, mtm.Item2.Name) as IEnumerable;
if (refitems == null) return null;
var reftb = fsql.CodeFirst.GetTableByEntity(mtm.Item1.RefEntityType);
foreach (var refitem in refitems)
{
var miditem = mtm.Item1.RefMiddleEntityType.CreateInstanceGetDefaultValue();
for (var a = 0; a < mtm.Item1.Columns.Count; a++)
{
var colval = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, mtm.Item1.Columns[a].CsName);
FreeSql.Extensions.EntityUtil.EntityUtilExtensions.SetPropertyValue(childTable, miditem, mtm.Item1.MiddleColumns[a].CsName, colval);
}
for (var a = 0; a < mtm.Item1.RefColumns.Count; a++)
{
var colval = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(reftb, refitem, mtm.Item1.RefColumns[a].CsName);
FreeSql.Extensions.EntityUtil.EntityUtilExtensions.SetPropertyValue(childTable, miditem, mtm.Item1.MiddleColumns[a + mtm.Item1.Columns.Count].CsName, colval);
}
midlist.Add(miditem);
}
return midlist;
}).Where(midlist => midlist != null).SelectMany(midlist => midlist).ToList();
midset.AttachRange(miditems);
LocalEach(midset, miditems, true);
}
else
{
var miditems = items.Select(item =>
{
var refitem = mtm.Item1.RefMiddleEntityType.CreateInstanceGetDefaultValue();
for (var a = 0; a < mtm.Item1.Columns.Count; a++)
{
var colval = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, mtm.Item1.Columns[a].CsName);
FreeSql.Extensions.EntityUtil.EntityUtilExtensions.SetPropertyValue(childTable, refitem, mtm.Item1.MiddleColumns[a].CsName, colval);
}
return refitem;
}).ToList();
var childs = midset.Select.Where(commonUtils.WhereItems(mtm.Item1.MiddleColumns.Take(mtm.Item1.Columns.Count).ToArray(), "a.", miditems)).ToList();
LocalEach(midset, childs, true);
}
}
}
if (dbset == rootDbSet)
{
if (CanRemove(data, true) == false) return;
foreach (var item in data) //防止清除 Identity/Guid
foreach (var item in data) //不直接调用 Remove防止清除 Identity/Guid
{
var state = CreateEntityState(item);
_states.TryRemove(state.Key, out var trystate);
if (inMemory) _db.OrmOriginal.ClearEntityPrimaryValueWithIdentityAndGuid(_entityType, item);
EnqueueToDbContext(DbContext.EntityChangeType.Delete, state);
}
DbContextFlushCommand();
}
else
{
if (dbset.CanRemove(items, true) == false) return;
foreach (var item in items) //防止清除 Identity/Guid
foreach (var item in items) //不直接调用 dbset.Remove防止清除 Identity/Guid
{
var state = dbset.CreateEntityState(item);
dbset._states.TryRemove(state.Key, out var trystate);
if (inMemory) _db.OrmOriginal.ClearEntityPrimaryValueWithIdentityAndGuid(dbset.EntityType, item);
dbset.EnqueueToDbContext(DbContext.EntityChangeType.Delete, state);
}
dbset.DbContextFlushCommand();
var rawset = _db.Set(dbset.EntityType);
var statesRemove = typeof(DbSet<>).MakeGenericType(dbset.EntityType).GetMethod("StatesRemoveByObjects", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(IEnumerable<object>) }, null);
if (statesRemove == null) throw new Exception("找不到方法 DbSet<>.StatesRemoveByObjects");
statesRemove.Invoke(rawset, new object[] { items });
}
returnDeleted.AddRange(items);
returnDeleted?.AddRange(items);
}
}
#endregion

View File

@ -242,7 +242,7 @@ namespace FreeSql.Extensions.EfCoreFluentApi
}
/// <summary>
/// 使用 Repository + EnableAddOrUpdateNavigate + NoneParameter 方式插入种子数据
/// 使用 Repository + EnableCascadeSave + NoneParameter 方式插入种子数据
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
@ -263,7 +263,7 @@ namespace FreeSql.Extensions.EfCoreFluentApi
if (_fsql.Select<object>().AsType(et).Any()) continue;
var repo = _fsql.GetRepository<object>();
repo.DbContextOptions.EnableAddOrUpdateNavigate = true;
repo.DbContextOptions.EnableCascadeSave = true;
repo.DbContextOptions.NoneParameter = true;
repo.AsType(et);
repo.Insert(sd);

View File

@ -329,7 +329,7 @@ namespace FreeSql.Extensions.EfCoreFluentApi
public EfCoreTableFluent<T> HasData(T data) => HasData(new[] { data });
/// <summary>
/// 使用 Repository + EnableAddOrUpdateNavigate + NoneParameter 方式插入种子数据
/// 使用 Repository + EnableCascadeSave + NoneParameter 方式插入种子数据
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
@ -350,7 +350,7 @@ namespace FreeSql.Extensions.EfCoreFluentApi
if (_fsql.Select<object>().AsType(et).Any()) continue;
var repo = _fsql.GetRepository<object>();
repo.DbContextOptions.EnableAddOrUpdateNavigate = true;
repo.DbContextOptions.EnableCascadeSave = true;
repo.DbContextOptions.NoneParameter = true;
repo.AsType(et);
repo.Insert(sd);

View File

@ -89,7 +89,7 @@
刷新队列中的命令
</summary>
</member>
<member name="P:FreeSql.DbContextOptions.EnableAddOrUpdateNavigate">
<member name="P:FreeSql.DbContextOptions.EnableCascadeSave">
<summary>
是否开启 一对一(OneToOne)、一对多(OneToMany)、多对多(ManyToMany) 级联保存功能<para></para>
<para></para>
@ -107,7 +107,7 @@
</member>
<member name="P:FreeSql.DbContextOptions.EnableAddOrUpdateNavigateList">
<summary>
因增加支持 OneToOne 级联保存,请了解机制,已改名为 EnableAddOrUpdateNavigate
因增加支持 OneToOne 级联保存,和基于内存的级联删除,已改名为 EnableCascadeSave
</summary>
</member>
<member name="P:FreeSql.DbContextOptions.NoneParameter">
@ -216,11 +216,11 @@
<param name="data">可选参数:手工传递最终的 data 值进行对比<para></para>默认:如果不传递,则使用 BeginEdit 传入的 data 引用进行对比</param>
<returns></returns>
</member>
<member name="M:FreeSql.DbSet`1.RemoveCascade(`0)">
<member name="M:FreeSql.DbSet`1.RemoveCascadeByDatabase(System.Linq.Expressions.Expression{System.Func{`0,System.Boolean}})">
<summary>
根据设置的导航属性,递归查询删除 OneToOne/OneToMany/ManyToMany 数据,并返回已删除的数据
根据设置的 OneToOne/OneToMany/ManyToMany 导航属性,级联查询所有的数据库记录,删除并返回它们
</summary>
<param name="data"></param>
<param name="predicate"></param>
<returns></returns>
</member>
<member name="M:FreeSql.Extensions.EfCoreFluentApi.EfCoreColumnFluent.Help">
@ -237,7 +237,7 @@
</member>
<member name="M:FreeSql.Extensions.EfCoreFluentApi.EfCoreTableFluent.HasData(System.Collections.Generic.IEnumerable{System.Object})">
<summary>
使用 Repository + EnableAddOrUpdateNavigate + NoneParameter 方式插入种子数据
使用 Repository + EnableCascadeSave + NoneParameter 方式插入种子数据
</summary>
<param name="data"></param>
<returns></returns>
@ -250,7 +250,7 @@
</member>
<member name="M:FreeSql.Extensions.EfCoreFluentApi.EfCoreTableFluent`1.HasData(System.Collections.Generic.IEnumerable{`0})">
<summary>
使用 Repository + EnableAddOrUpdateNavigate + NoneParameter 方式插入种子数据
使用 Repository + EnableCascadeSave + NoneParameter 方式插入种子数据
</summary>
<param name="data"></param>
<returns></returns>
@ -360,11 +360,11 @@
<param name="entity">实体对象</param>
<param name="propertyName">属性名</param>
</member>
<member name="M:FreeSql.IBaseRepository`1.DeleteCascade(`0)">
<member name="M:FreeSql.IBaseRepository`1.RemoveCascadeByDatabase(System.Linq.Expressions.Expression{System.Func{`0,System.Boolean}})">
<summary>
根据设置的导航属性,递归查询删除 OneToOne/OneToMany/ManyToMany 数据,并返回已删除的数据
根据设置的 OneToOne/OneToMany/ManyToMany 导航属性,级联查询所有的数据库记录,删除并返回它们
</summary>
<param name="entity"></param>
<param name="predicate"></param>
<returns></returns>
</member>
<member name="M:FreeSql.IBaseRepository`1.BeginEdit(System.Collections.Generic.List{`0})">
@ -559,14 +559,5 @@
<param name="that"></param>
<returns></returns>
</member>
<member name="M:Microsoft.Extensions.DependencyInjection.FreeSqlRepositoryDependencyInjection.AddFreeRepository(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Action{FreeSql.FluentDataFilter},System.Reflection.Assembly[])">
<summary>
批量注入 Repository可以参考代码自行调整
</summary>
<param name="services"></param>
<param name="globalDataFilter"></param>
<param name="assemblies"></param>
<returns></returns>
</member>
</members>
</doc>

View File

@ -94,9 +94,12 @@ namespace FreeSql
_dbset.RemoveRange(entitys);
return _db.SaveChanges();
}
public List<object> DeleteCascade(TEntity entity) => _dbset.RemoveCascade(entity);
public List<object> DeleteCascade(IEnumerable<TEntity> entitys) => _dbset.RemoveRangeCascade(entitys);
public List<object> DeleteCascade(Expression<Func<TEntity, bool>> predicate) => _dbset.RemoveCascade(predicate);
public List<object> RemoveCascadeByDatabase(Expression<Func<TEntity, bool>> predicate)
{
var list = _dbset.RemoveCascadeByDatabase(predicate);
var affrows = _db.SaveChanges();
return list;
}
public virtual TEntity Insert(TEntity entity)
{
@ -192,7 +195,6 @@ namespace FreeSql
return ret;
}
public virtual List<object> DeleteCascade(TKey id) => _dbset.RemoveCascade(Find(id));
public virtual int Delete(TKey id) => Delete(CheckTKeyAndReturnIdEntity(id));
public virtual TEntity Find(TKey id) => _dbset.OrmSelectInternal(CheckTKeyAndReturnIdEntity(id)).ToOne();
public TEntity Get(TKey id) => _dbset.OrmSelectInternal(CheckTKeyAndReturnIdEntity(id)).ToOne();

View File

@ -32,9 +32,12 @@ namespace FreeSql
_dbset.RemoveRange(entitys);
return _db.SaveChangesAsync(cancellationToken);
}
public virtual Task<List<object>> DeleteCascade(TEntity entity, CancellationToken cancellationToken = default) => _dbset.RemoveCascadeAsync(entity, cancellationToken);
public virtual Task<List<object>> DeleteCascade(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => _dbset.RemoveRangeCascadeAsync(entitys, cancellationToken);
public virtual Task<List<object>> DeleteCascade(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => _dbset.RemoveCascadeAsync(predicate, cancellationToken);
async public virtual Task<List<object>> RemoveCascadeByDatabaseAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
{
var list = await _dbset.RemoveCascadeByDatabaseAsync(predicate, cancellationToken);
var affrows = await _db.SaveChangesAsync(cancellationToken);
return list;
}
async public virtual Task<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default)
{
@ -76,7 +79,6 @@ namespace FreeSql
partial class BaseRepository<TEntity, TKey>
{
public virtual Task<List<object>> DeleteCascadeAsync(TKey id, CancellationToken cancellationToken = default) => _dbset.RemoveCascadeAsync(Find(id), cancellationToken);
public virtual Task<int> DeleteAsync(TKey id, CancellationToken cancellationToken = default) => DeleteAsync(CheckTKeyAndReturnIdEntity(id), cancellationToken);
public virtual Task<TEntity> FindAsync(TKey id, CancellationToken cancellationToken = default) => _dbset.OrmSelectInternal(CheckTKeyAndReturnIdEntity(id)).ToOneAsync(cancellationToken);
public Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default) => _dbset.OrmSelectInternal(CheckTKeyAndReturnIdEntity(id)).ToOneAsync(cancellationToken);

View File

@ -86,13 +86,11 @@ namespace FreeSql
int Delete(IEnumerable<TEntity> entitys);
int Delete(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// 根据设置的导航属性,递归查询删除 OneToOne/OneToMany/ManyToMany 数据,并返回已删除的数据
/// 根据设置的 OneToOne/OneToMany/ManyToMany 导航属性,级联查询所有的数据库记录,删除并返回它们
/// </summary>
/// <param name="entity"></param>
/// <param name="predicate"></param>
/// <returns></returns>
List<object> DeleteCascade(TEntity entity);
List<object> DeleteCascade(IEnumerable<TEntity> entitys);
List<object> DeleteCascade(Expression<Func<TEntity, bool>> predicate);
List<object> RemoveCascadeByDatabase(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// 开始编辑数据,然后调用方法 EndEdit 分析出添加、修改、删除 SQL 语句进行执行<para></para>
@ -124,9 +122,7 @@ namespace FreeSql
Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default);
Task<int> DeleteAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default);
Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
Task<List<object>> DeleteCascade(TEntity entity, CancellationToken cancellationToken = default);
Task<List<object>> DeleteCascade(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default);
Task<List<object>> DeleteCascade(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
Task<List<object>> RemoveCascadeByDatabaseAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
#endif
}
@ -136,14 +132,12 @@ namespace FreeSql
TEntity Get(TKey id);
TEntity Find(TKey id);
int Delete(TKey id);
List<object> DeleteCascade(TKey id);
#if net40
#else
Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default);
Task<TEntity> FindAsync(TKey id, CancellationToken cancellationToken = default);
Task<int> DeleteAsync(TKey id, CancellationToken cancellationToken = default);
Task<List<object>> DeleteCascadeAsync(TKey id, CancellationToken cancellationToken = default);
#endif
}
}