mirror of
				https://github.com/nsnail/FreeSql.git
				synced 2025-11-04 17:20:49 +08:00 
			
		
		
		
	- 增加 DbSet/Repository DeleteCascadeAsync 异步方法;
This commit is contained in:
		@@ -1,4 +1,5 @@
 | 
				
			|||||||
using FreeSql.Extensions.EntityUtil;
 | 
					using FreeSql.Extensions.EntityUtil;
 | 
				
			||||||
 | 
					using FreeSql.Internal.Model;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
using System.Collections;
 | 
					using System.Collections;
 | 
				
			||||||
using System.Collections.Concurrent;
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
@@ -493,6 +494,159 @@ namespace FreeSql
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        #endregion
 | 
					        #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)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            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);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        #endregion
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
@@ -105,6 +105,11 @@
 | 
				
			|||||||
            - 属性集合不为空时,与数据库存在的关联数据(中间表)完全对比,计算出应该删除和添加的记录
 | 
					            - 属性集合不为空时,与数据库存在的关联数据(中间表)完全对比,计算出应该删除和添加的记录
 | 
				
			||||||
            </summary>
 | 
					            </summary>
 | 
				
			||||||
        </member>
 | 
					        </member>
 | 
				
			||||||
 | 
					        <member name="P:FreeSql.DbContextOptions.EnableAddOrUpdateNavigateList">
 | 
				
			||||||
 | 
					            <summary>
 | 
				
			||||||
 | 
					            因增加支持 OneToOne 级联保存,请了解机制,已改名为 EnableAddOrUpdateNavigate
 | 
				
			||||||
 | 
					            </summary>
 | 
				
			||||||
 | 
					        </member>
 | 
				
			||||||
        <member name="P:FreeSql.DbContextOptions.NoneParameter">
 | 
					        <member name="P:FreeSql.DbContextOptions.NoneParameter">
 | 
				
			||||||
            <summary>
 | 
					            <summary>
 | 
				
			||||||
            使用无参数化设置(对应 IInsert/IUpdate)
 | 
					            使用无参数化设置(对应 IInsert/IUpdate)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,6 +32,9 @@ namespace FreeSql
 | 
				
			|||||||
            _dbset.RemoveRange(entitys);
 | 
					            _dbset.RemoveRange(entitys);
 | 
				
			||||||
            return _db.SaveChangesAsync(cancellationToken);
 | 
					            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<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default)
 | 
					        async public virtual Task<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
@@ -73,6 +76,7 @@ namespace FreeSql
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    partial class BaseRepository<TEntity, TKey>
 | 
					    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<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 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);
 | 
					        public Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default) => _dbset.OrmSelectInternal(CheckTKeyAndReturnIdEntity(id)).ToOneAsync(cancellationToken);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -124,6 +124,9 @@ namespace FreeSql
 | 
				
			|||||||
        Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default);
 | 
					        Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default);
 | 
				
			||||||
        Task<int> DeleteAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default);
 | 
					        Task<int> DeleteAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default);
 | 
				
			||||||
        Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, 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);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -140,6 +143,7 @@ namespace FreeSql
 | 
				
			|||||||
        Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default);
 | 
					        Task<TEntity> GetAsync(TKey id, CancellationToken cancellationToken = default);
 | 
				
			||||||
        Task<TEntity> FindAsync(TKey id, CancellationToken cancellationToken = default);
 | 
					        Task<TEntity> FindAsync(TKey id, CancellationToken cancellationToken = default);
 | 
				
			||||||
        Task<int> DeleteAsync(TKey id, CancellationToken cancellationToken = default);
 | 
					        Task<int> DeleteAsync(TKey id, CancellationToken cancellationToken = default);
 | 
				
			||||||
 | 
					        Task<List<object>> DeleteCascadeAsync(TKey id, CancellationToken cancellationToken = default);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user