mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-06-18 20:08:15 +08:00
- 调整 DbSet/Repository DeleteCascadeByDatabase/Delete 区别于数据库/内存的级联删除;#609
This commit is contained in:
@ -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
|
||||
}
|
||||
|
Reference in New Issue
Block a user