mirror of
				https://github.com/nsnail/FreeSql.git
				synced 2025-11-04 17:20:49 +08:00 
			
		
		
		
	- 增加 DbSet/Repository DeleteCascade 级联删除功能;#609
This commit is contained in:
		@@ -17,7 +17,7 @@ namespace FreeSql
 | 
			
		||||
        /// - 保存的时候,由于数据库中记录非常之多,那么只想保存子表的部分数据,或者只需要添加,如何操作?<para></para>
 | 
			
		||||
        /// <para></para>
 | 
			
		||||
        /// 【多对多】模型下,我们对中间表的保存是完整对比操作,对外部实体的操作只作新增(*注意不会更新)<para></para>
 | 
			
		||||
        /// - 属性集合为空时,删除他们的所有关联数据(中间表)<para></para>
 | 
			
		||||
        /// - 属性集合为空时(!=null),删除他们的所有关联数据(中间表)<para></para>
 | 
			
		||||
        /// - 属性集合不为空时,与数据库存在的关联数据(中间表)完全对比,计算出应该删除和添加的记录
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public bool EnableAddOrUpdateNavigateList { get; set; } = false;
 | 
			
		||||
 
 | 
			
		||||
@@ -209,16 +209,26 @@ namespace FreeSql
 | 
			
		||||
 | 
			
		||||
                var tref = _table.GetTableRef(prop.Name, false); //防止非正常的导航属性报错
 | 
			
		||||
                if (tref == null) return;
 | 
			
		||||
                DbSet<object> refSet = null;
 | 
			
		||||
                switch (tref.RefType)
 | 
			
		||||
                {
 | 
			
		||||
                    case Internal.Model.TableRefType.OneToOne:
 | 
			
		||||
                        //var propValItem = GetItemValue(item, prop);
 | 
			
		||||
                        //for (var colidx = 0; colidx < tref.Columns.Count; colidx++)
 | 
			
		||||
                        //{
 | 
			
		||||
                        //    var val = FreeSql.Internal.Utils.GetDataReaderValue(tref.RefColumns[colidx].CsType, _db.OrmOriginal.GetEntityValueWithPropertyName(_table.Type, item, tref.Columns[colidx].CsName));
 | 
			
		||||
                        //    _db.OrmOriginal.SetEntityValueWithPropertyName(tref.RefEntityType, propValItem, tref.RefColumns[colidx].CsName, val);
 | 
			
		||||
                        //}
 | 
			
		||||
                        //if (isAdd) await refSet.AddAsync(propValItem);
 | 
			
		||||
                        //else await refSet.AddOrUpdateAsync(propValItem);
 | 
			
		||||
                        //return;
 | 
			
		||||
                    case Internal.Model.TableRefType.ManyToOne:
 | 
			
		||||
                        return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var propValEach = GetItemValue(item, prop) as IEnumerable;
 | 
			
		||||
                if (propValEach == null) return;
 | 
			
		||||
                DbSet<object> refSet = GetDbSetObject(tref.RefEntityType);
 | 
			
		||||
                refSet = GetDbSetObject(tref.RefEntityType);
 | 
			
		||||
                switch (tref.RefType)
 | 
			
		||||
                {
 | 
			
		||||
                    case Internal.Model.TableRefType.ManyToMany:
 | 
			
		||||
 
 | 
			
		||||
@@ -221,16 +221,26 @@ namespace FreeSql
 | 
			
		||||
 | 
			
		||||
                var tref = _table.GetTableRef(prop.Name, false); //防止非正常的导航属性报错
 | 
			
		||||
                if (tref == null) return;
 | 
			
		||||
                DbSet<object> refSet = null;
 | 
			
		||||
                switch (tref.RefType)
 | 
			
		||||
                {
 | 
			
		||||
                    case Internal.Model.TableRefType.OneToOne:
 | 
			
		||||
                        //var propValItem = GetItemValue(item, prop);
 | 
			
		||||
                        //for (var colidx = 0; colidx < tref.Columns.Count; colidx++)
 | 
			
		||||
                        //{
 | 
			
		||||
                        //    var val = FreeSql.Internal.Utils.GetDataReaderValue(tref.RefColumns[colidx].CsType, _db.OrmOriginal.GetEntityValueWithPropertyName(_table.Type, item, tref.Columns[colidx].CsName));
 | 
			
		||||
                        //    _db.OrmOriginal.SetEntityValueWithPropertyName(tref.RefEntityType, propValItem, tref.RefColumns[colidx].CsName, val);
 | 
			
		||||
                        //}
 | 
			
		||||
                        //if (isAdd) refSet.Add(propValItem);
 | 
			
		||||
                        //else refSet.AddOrUpdate(propValItem);
 | 
			
		||||
                        //return;
 | 
			
		||||
                    case Internal.Model.TableRefType.ManyToOne:
 | 
			
		||||
                        return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var propValEach = GetItemValue(item, prop) as IEnumerable;
 | 
			
		||||
                if (propValEach == null) return;
 | 
			
		||||
                DbSet<object> refSet = GetDbSetObject(tref.RefEntityType);
 | 
			
		||||
                refSet = GetDbSetObject(tref.RefEntityType);
 | 
			
		||||
                switch (tref.RefType)
 | 
			
		||||
                {
 | 
			
		||||
                    case Internal.Model.TableRefType.ManyToMany:
 | 
			
		||||
@@ -641,5 +651,163 @@ namespace FreeSql
 | 
			
		||||
            return _db._affrows - beforeAffrows;
 | 
			
		||||
        }
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
        #region RemoveCascade
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 根据设置的导航属性,递归查询删除 OneToOne/OneToMany/ManyToMany 数据,并返回已删除的数据
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="data"></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)
 | 
			
		||||
        {
 | 
			
		||||
            var returnDeleted = 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
 | 
			
		||||
                {
 | 
			
		||||
                    var state = CreateEntityState(item);
 | 
			
		||||
                    _states.TryRemove(state.Key, out var trystate);
 | 
			
		||||
 | 
			
		||||
                    EnqueueToDbContext(DbContext.EntityChangeType.Delete, state);
 | 
			
		||||
                }
 | 
			
		||||
                DbContextFlushCommand();
 | 
			
		||||
                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);
 | 
			
		||||
            LocalEach(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();
 | 
			
		||||
            }
 | 
			
		||||
            void LocalEach(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 = childDbSet.Select.Where(commonUtils.WhereItems(oto.Item1.RefColumns.ToArray(), "a.", refitems)).ToList();
 | 
			
		||||
                        LocalEach(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 = childDbSet.Select.Where(commonUtils.WhereItems(otm.Item1.RefColumns.ToArray(), "a.", refitems)).ToList();
 | 
			
		||||
                        LocalEach(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 = childDbSet.Select.Where(commonUtils.WhereItems(mtm.Item1.MiddleColumns.Take(mtm.Item1.Columns.Count).ToArray(), "a.", miditems)).ToList();
 | 
			
		||||
                        LocalEach(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);
 | 
			
		||||
                    }
 | 
			
		||||
                    DbContextFlushCommand();
 | 
			
		||||
                }
 | 
			
		||||
                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);
 | 
			
		||||
                    }
 | 
			
		||||
                    dbset.DbContextFlushCommand();
 | 
			
		||||
                }
 | 
			
		||||
                returnDeleted.AddRange(items);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        #endregion
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -99,7 +99,7 @@
 | 
			
		||||
            - 保存的时候,由于数据库中记录非常之多,那么只想保存子表的部分数据,或者只需要添加,如何操作?<para></para>
 | 
			
		||||
            <para></para>
 | 
			
		||||
            【多对多】模型下,我们对中间表的保存是完整对比操作,对外部实体的操作只作新增(*注意不会更新)<para></para>
 | 
			
		||||
            - 属性集合为空时,删除他们的所有关联数据(中间表)<para></para>
 | 
			
		||||
            - 属性集合为空时(!=null),删除他们的所有关联数据(中间表)<para></para>
 | 
			
		||||
            - 属性集合不为空时,与数据库存在的关联数据(中间表)完全对比,计算出应该删除和添加的记录
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
@@ -209,6 +209,13 @@
 | 
			
		||||
            <param name="data">可选参数:手工传递最终的 data 值进行对比<para></para>默认:如果不传递,则使用 BeginEdit 传入的 data 引用进行对比</param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:FreeSql.DbSet`1.RemoveCascade(`0)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            根据设置的导航属性,递归查询删除 OneToOne/OneToMany/ManyToMany 数据,并返回已删除的数据
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="data"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:FreeSql.Extensions.EfCoreFluentApi.EfCoreColumnFluent.Help">
 | 
			
		||||
            <summary>
 | 
			
		||||
            使用 FreeSql FluentApi 方法,当 EFCore FluentApi 方法无法表示的时候使用
 | 
			
		||||
@@ -346,6 +353,13 @@
 | 
			
		||||
            <param name="entity">实体对象</param>
 | 
			
		||||
            <param name="propertyName">属性名</param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:FreeSql.IBaseRepository`1.DeleteCascade(`0)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            根据设置的导航属性,递归查询删除 OneToOne/OneToMany/ManyToMany 数据,并返回已删除的数据
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="entity"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:FreeSql.IBaseRepository`1.BeginEdit(System.Collections.Generic.List{`0})">
 | 
			
		||||
            <summary>
 | 
			
		||||
            开始编辑数据,然后调用方法 EndEdit 分析出添加、修改、删除 SQL 语句进行执行<para></para>
 | 
			
		||||
@@ -538,5 +552,14 @@
 | 
			
		||||
            <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>
 | 
			
		||||
 
 | 
			
		||||
@@ -94,6 +94,9 @@ 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 virtual TEntity Insert(TEntity entity)
 | 
			
		||||
        {
 | 
			
		||||
@@ -189,6 +192,7 @@ 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();
 | 
			
		||||
 
 | 
			
		||||
@@ -85,6 +85,14 @@ namespace FreeSql
 | 
			
		||||
        int Delete(TEntity entity);
 | 
			
		||||
        int Delete(IEnumerable<TEntity> entitys);
 | 
			
		||||
        int Delete(Expression<Func<TEntity, bool>> predicate);
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 根据设置的导航属性,递归查询删除 OneToOne/OneToMany/ManyToMany 数据,并返回已删除的数据
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="entity"></param>
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        List<object> DeleteCascade(TEntity entity);
 | 
			
		||||
        List<object> DeleteCascade(IEnumerable<TEntity> entitys);
 | 
			
		||||
        List<object> DeleteCascade(Expression<Func<TEntity, bool>> predicate);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 开始编辑数据,然后调用方法 EndEdit 分析出添加、修改、删除 SQL 语句进行执行<para></para>
 | 
			
		||||
@@ -125,6 +133,7 @@ namespace FreeSql
 | 
			
		||||
        TEntity Get(TKey id);
 | 
			
		||||
        TEntity Find(TKey id);
 | 
			
		||||
        int Delete(TKey id);
 | 
			
		||||
        List<object> DeleteCascade(TKey id);
 | 
			
		||||
 | 
			
		||||
#if net40
 | 
			
		||||
#else
 | 
			
		||||
 
 | 
			
		||||
@@ -9,6 +9,252 @@ namespace FreeSql.Tests
 | 
			
		||||
{
 | 
			
		||||
    public class RepositoryTests
 | 
			
		||||
    {
 | 
			
		||||
        [Fact]
 | 
			
		||||
        public void DeleteCascade()
 | 
			
		||||
        {
 | 
			
		||||
            var fsql = g.sqlite;
 | 
			
		||||
            var groupRepo = fsql.GetRepository<DeleteCascadeUserGroup>();
 | 
			
		||||
            var userRepo = fsql.GetRepository<DeleteCascadeUser>();
 | 
			
		||||
            //OneToOne
 | 
			
		||||
            fsql.Delete<DeleteCascadeUser>().Where("1=1").ExecuteAffrows();
 | 
			
		||||
            fsql.Delete<DeleteCascadeUserExt>().Where("1=1").ExecuteAffrows();
 | 
			
		||||
            var user = new DeleteCascadeUser { Username = "admin01", Password = "pwd01" };
 | 
			
		||||
            userRepo.Insert(user);
 | 
			
		||||
            var userExt = new DeleteCascadeUserExt { UserId = user.Id, Remark = "用户备注01" };
 | 
			
		||||
            Assert.Equal(1, fsql.Insert(userExt).ExecuteAffrows());
 | 
			
		||||
            var ret = userRepo.DeleteCascade(user);
 | 
			
		||||
            Assert.Equal(2, ret.Count);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserExt>(ret[0]);
 | 
			
		||||
            Assert.Equal(userExt.UserId, (ret[0] as DeleteCascadeUserExt).UserId);
 | 
			
		||||
            Assert.Equal(userExt.Remark, (ret[0] as DeleteCascadeUserExt).Remark);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUser>(ret[1]);
 | 
			
		||||
            Assert.Equal(user.Id, (ret[1] as DeleteCascadeUser).Id);
 | 
			
		||||
            Assert.Equal(user.Username, (ret[1] as DeleteCascadeUser).Username);
 | 
			
		||||
            Assert.Equal(user.Password, (ret[1] as DeleteCascadeUser).Password);
 | 
			
		||||
 | 
			
		||||
            //OneToOne 先删除 UserExt
 | 
			
		||||
            fsql.Delete<DeleteCascadeUser>().Where("1=1").ExecuteAffrows();
 | 
			
		||||
            fsql.Delete<DeleteCascadeUserExt>().Where("1=1").ExecuteAffrows();
 | 
			
		||||
            user = new DeleteCascadeUser { Username = "admin01", Password = "pwd01" };
 | 
			
		||||
            userRepo.Insert(user);
 | 
			
		||||
            userExt = new DeleteCascadeUserExt { UserId = user.Id, Remark = "用户备注01" };
 | 
			
		||||
            Assert.Equal(1, fsql.Insert(userExt).ExecuteAffrows());
 | 
			
		||||
            ret = fsql.GetRepository<DeleteCascadeUserExt>().DeleteCascade(userExt);
 | 
			
		||||
            Assert.Equal(2, ret.Count);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserExt>(ret[1]);
 | 
			
		||||
            Assert.Equal(userExt.UserId, (ret[1] as DeleteCascadeUserExt).UserId);
 | 
			
		||||
            Assert.Equal(userExt.Remark, (ret[1] as DeleteCascadeUserExt).Remark);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUser>(ret[0]);
 | 
			
		||||
            Assert.Equal(user.Id, (ret[0] as DeleteCascadeUser).Id);
 | 
			
		||||
            Assert.Equal(user.Username, (ret[0] as DeleteCascadeUser).Username);
 | 
			
		||||
            Assert.Equal(user.Password, (ret[0] as DeleteCascadeUser).Password);
 | 
			
		||||
 | 
			
		||||
            //OneToMany
 | 
			
		||||
            fsql.Delete<DeleteCascadeUserGroup>().Where("1=1").ExecuteAffrows();
 | 
			
		||||
            fsql.Delete<DeleteCascadeUser>().Where("1=1").ExecuteAffrows();
 | 
			
		||||
            fsql.Delete<DeleteCascadeUserExt>().Where("1=1").ExecuteAffrows();
 | 
			
		||||
            groupRepo.DbContextOptions.EnableAddOrUpdateNavigateList = true;
 | 
			
		||||
            var group = new DeleteCascadeUserGroup
 | 
			
		||||
            {
 | 
			
		||||
                GroupName = "group01",
 | 
			
		||||
                Users = new List<DeleteCascadeUser>
 | 
			
		||||
                {
 | 
			
		||||
                    new DeleteCascadeUser { Username = "admin01", Password = "pwd01" },
 | 
			
		||||
                    new DeleteCascadeUser { Username = "admin02", Password = "pwd02" },
 | 
			
		||||
                    new DeleteCascadeUser { Username = "admin03", Password = "pwd03" },
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            groupRepo.Insert(group);
 | 
			
		||||
            Assert.Equal(group.Id, group.Users[0].GroupId);
 | 
			
		||||
            Assert.Equal(group.Id, group.Users[1].GroupId);
 | 
			
		||||
            Assert.Equal(group.Id, group.Users[2].GroupId);
 | 
			
		||||
            var userExt0 = new DeleteCascadeUserExt { UserId = group.Users[0].Id, Remark = "用户备注01" };
 | 
			
		||||
            Assert.Equal(1, fsql.Insert(userExt0).ExecuteAffrows());
 | 
			
		||||
            var userExt1 = new DeleteCascadeUserExt { UserId = group.Users[1].Id, Remark = "用户备注02" };
 | 
			
		||||
            Assert.Equal(1, fsql.Insert(userExt1).ExecuteAffrows());
 | 
			
		||||
            var userExt2 = new DeleteCascadeUserExt { UserId = group.Users[2].Id, Remark = "用户备注03" };
 | 
			
		||||
            Assert.Equal(1, fsql.Insert(userExt2).ExecuteAffrows());
 | 
			
		||||
            ret = groupRepo.DeleteCascade(group);
 | 
			
		||||
            Assert.Equal(7, ret.Count);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserExt>(ret[0]);
 | 
			
		||||
            Assert.Equal(userExt0.UserId, (ret[0] as DeleteCascadeUserExt).UserId);
 | 
			
		||||
            Assert.Equal(userExt0.Remark, (ret[0] as DeleteCascadeUserExt).Remark);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserExt>(ret[1]);
 | 
			
		||||
            Assert.Equal(userExt1.UserId, (ret[1] as DeleteCascadeUserExt).UserId);
 | 
			
		||||
            Assert.Equal(userExt1.Remark, (ret[1] as DeleteCascadeUserExt).Remark);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserExt>(ret[2]);
 | 
			
		||||
            Assert.Equal(userExt2.UserId, (ret[2] as DeleteCascadeUserExt).UserId);
 | 
			
		||||
            Assert.Equal(userExt2.Remark, (ret[2] as DeleteCascadeUserExt).Remark);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUser>(ret[3]);
 | 
			
		||||
            Assert.Equal(group.Users[0].Id, (ret[3] as DeleteCascadeUser).Id);
 | 
			
		||||
            Assert.Equal(group.Users[0].Username, (ret[3] as DeleteCascadeUser).Username);
 | 
			
		||||
            Assert.Equal(group.Users[0].Password, (ret[3] as DeleteCascadeUser).Password);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUser>(ret[4]);
 | 
			
		||||
            Assert.Equal(group.Users[1].Id, (ret[4] as DeleteCascadeUser).Id);
 | 
			
		||||
            Assert.Equal(group.Users[1].Username, (ret[4] as DeleteCascadeUser).Username);
 | 
			
		||||
            Assert.Equal(group.Users[1].Password, (ret[4] as DeleteCascadeUser).Password);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUser>(ret[5]);
 | 
			
		||||
            Assert.Equal(group.Users[2].Id, (ret[5] as DeleteCascadeUser).Id);
 | 
			
		||||
            Assert.Equal(group.Users[2].Username, (ret[5] as DeleteCascadeUser).Username);
 | 
			
		||||
            Assert.Equal(group.Users[2].Password, (ret[5] as DeleteCascadeUser).Password);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserGroup>(ret[6]);
 | 
			
		||||
            Assert.Equal(group.Id, (ret[6] as DeleteCascadeUserGroup).Id);
 | 
			
		||||
            Assert.Equal(group.GroupName, (ret[6] as DeleteCascadeUserGroup).GroupName);
 | 
			
		||||
 | 
			
		||||
            //ManyToMany
 | 
			
		||||
            fsql.Delete<DeleteCascadeUserGroup>().Where("1=1").ExecuteAffrows();
 | 
			
		||||
            fsql.Delete<DeleteCascadeUser>().Where("1=1").ExecuteAffrows();
 | 
			
		||||
            fsql.Delete<DeleteCascadeUserExt>().Where("1=1").ExecuteAffrows();
 | 
			
		||||
            fsql.Delete<DeleteCascadeTag>().Where("1=1").ExecuteAffrows();
 | 
			
		||||
            fsql.Delete<DeleteCascadeUserTag>().Where("1=1").ExecuteAffrows();
 | 
			
		||||
            var tags = new[] {
 | 
			
		||||
                new DeleteCascadeTag { TagName = "tag01" },
 | 
			
		||||
                new DeleteCascadeTag { TagName = "tag02" },
 | 
			
		||||
                new DeleteCascadeTag { TagName = "tag03" },
 | 
			
		||||
                new DeleteCascadeTag { TagName = "tag04" },
 | 
			
		||||
                new DeleteCascadeTag { TagName = "tag05" },
 | 
			
		||||
                new DeleteCascadeTag { TagName = "tag06" },
 | 
			
		||||
                new DeleteCascadeTag { TagName = "tag07" },
 | 
			
		||||
                new DeleteCascadeTag { TagName = "tag08" },
 | 
			
		||||
            };
 | 
			
		||||
            fsql.GetRepository<DeleteCascadeTag>().Insert(tags);
 | 
			
		||||
            groupRepo.DbContextOptions.EnableAddOrUpdateNavigateList = true;
 | 
			
		||||
            group = new DeleteCascadeUserGroup
 | 
			
		||||
            {
 | 
			
		||||
                GroupName = "group01",
 | 
			
		||||
                Users = new List<DeleteCascadeUser>
 | 
			
		||||
                {
 | 
			
		||||
                    new DeleteCascadeUser { Username = "admin01", Password = "pwd01", Tags = new List<DeleteCascadeTag> { tags[0], tags[2], tags[3], tags[6] } },
 | 
			
		||||
                    new DeleteCascadeUser { Username = "admin02", Password = "pwd02", Tags = new List<DeleteCascadeTag> { tags[1], tags[2], tags[5] } },
 | 
			
		||||
                    new DeleteCascadeUser { Username = "admin03", Password = "pwd03", Tags = new List<DeleteCascadeTag> { tags[3], tags[4], tags[6], tags[7] } },
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            groupRepo.Insert(group);
 | 
			
		||||
            Assert.Equal(group.Id, group.Users[0].GroupId);
 | 
			
		||||
            Assert.Equal(group.Id, group.Users[1].GroupId);
 | 
			
		||||
            Assert.Equal(group.Id, group.Users[2].GroupId);
 | 
			
		||||
            userExt0 = new DeleteCascadeUserExt { UserId = group.Users[0].Id, Remark = "用户备注01" };
 | 
			
		||||
            Assert.Equal(1, fsql.Insert(userExt0).ExecuteAffrows());
 | 
			
		||||
            userExt1 = new DeleteCascadeUserExt { UserId = group.Users[1].Id, Remark = "用户备注02" };
 | 
			
		||||
            Assert.Equal(1, fsql.Insert(userExt1).ExecuteAffrows());
 | 
			
		||||
            userExt2 = new DeleteCascadeUserExt { UserId = group.Users[2].Id, Remark = "用户备注03" };
 | 
			
		||||
            Assert.Equal(1, fsql.Insert(userExt2).ExecuteAffrows());
 | 
			
		||||
            ret = groupRepo.DeleteCascade(group);
 | 
			
		||||
            Assert.Equal(18, ret.Count);
 | 
			
		||||
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserExt>(ret[0]);
 | 
			
		||||
            Assert.Equal(userExt0.UserId, (ret[0] as DeleteCascadeUserExt).UserId);
 | 
			
		||||
            Assert.Equal(userExt0.Remark, (ret[0] as DeleteCascadeUserExt).Remark);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserExt>(ret[1]);
 | 
			
		||||
            Assert.Equal(userExt1.UserId, (ret[1] as DeleteCascadeUserExt).UserId);
 | 
			
		||||
            Assert.Equal(userExt1.Remark, (ret[1] as DeleteCascadeUserExt).Remark);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserExt>(ret[2]);
 | 
			
		||||
            Assert.Equal(userExt2.UserId, (ret[2] as DeleteCascadeUserExt).UserId);
 | 
			
		||||
            Assert.Equal(userExt2.Remark, (ret[2] as DeleteCascadeUserExt).Remark);
 | 
			
		||||
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserTag>(ret[3]);
 | 
			
		||||
            Assert.Equal(group.Users[0].Id, (ret[3] as DeleteCascadeUserTag).UserId);
 | 
			
		||||
            Assert.Equal(tags[0].Id, (ret[3] as DeleteCascadeUserTag).TagId);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserTag>(ret[4]);
 | 
			
		||||
            Assert.Equal(group.Users[0].Id, (ret[4] as DeleteCascadeUserTag).UserId);
 | 
			
		||||
            Assert.Equal(tags[2].Id, (ret[4] as DeleteCascadeUserTag).TagId);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserTag>(ret[5]);
 | 
			
		||||
            Assert.Equal(group.Users[0].Id, (ret[5] as DeleteCascadeUserTag).UserId);
 | 
			
		||||
            Assert.Equal(tags[3].Id, (ret[5] as DeleteCascadeUserTag).TagId);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserTag>(ret[6]);
 | 
			
		||||
            Assert.Equal(group.Users[0].Id, (ret[6] as DeleteCascadeUserTag).UserId);
 | 
			
		||||
            Assert.Equal(tags[6].Id, (ret[6] as DeleteCascadeUserTag).TagId);
 | 
			
		||||
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserTag>(ret[7]);
 | 
			
		||||
            Assert.Equal(group.Users[1].Id, (ret[7] as DeleteCascadeUserTag).UserId);
 | 
			
		||||
            Assert.Equal(tags[1].Id, (ret[7] as DeleteCascadeUserTag).TagId);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserTag>(ret[8]);
 | 
			
		||||
            Assert.Equal(group.Users[1].Id, (ret[8] as DeleteCascadeUserTag).UserId);
 | 
			
		||||
            Assert.Equal(tags[2].Id, (ret[8] as DeleteCascadeUserTag).TagId);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserTag>(ret[9]);
 | 
			
		||||
            Assert.Equal(group.Users[1].Id, (ret[9] as DeleteCascadeUserTag).UserId);
 | 
			
		||||
            Assert.Equal(tags[5].Id, (ret[9] as DeleteCascadeUserTag).TagId);
 | 
			
		||||
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserTag>(ret[10]);
 | 
			
		||||
            Assert.Equal(group.Users[2].Id, (ret[10] as DeleteCascadeUserTag).UserId);
 | 
			
		||||
            Assert.Equal(tags[3].Id, (ret[10] as DeleteCascadeUserTag).TagId);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserTag>(ret[11]);
 | 
			
		||||
            Assert.Equal(group.Users[2].Id, (ret[11] as DeleteCascadeUserTag).UserId);
 | 
			
		||||
            Assert.Equal(tags[4].Id, (ret[11] as DeleteCascadeUserTag).TagId);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserTag>(ret[12]);
 | 
			
		||||
            Assert.Equal(group.Users[2].Id, (ret[12] as DeleteCascadeUserTag).UserId);
 | 
			
		||||
            Assert.Equal(tags[6].Id, (ret[12] as DeleteCascadeUserTag).TagId);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserTag>(ret[13]);
 | 
			
		||||
            Assert.Equal(group.Users[2].Id, (ret[13] as DeleteCascadeUserTag).UserId);
 | 
			
		||||
            Assert.Equal(tags[7].Id, (ret[13] as DeleteCascadeUserTag).TagId);
 | 
			
		||||
 | 
			
		||||
            Assert.IsType<DeleteCascadeUser>(ret[14]);
 | 
			
		||||
            Assert.Equal(group.Users[0].Id, (ret[14] as DeleteCascadeUser).Id);
 | 
			
		||||
            Assert.Equal(group.Users[0].Username, (ret[14] as DeleteCascadeUser).Username);
 | 
			
		||||
            Assert.Equal(group.Users[0].Password, (ret[14] as DeleteCascadeUser).Password);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUser>(ret[15]);
 | 
			
		||||
            Assert.Equal(group.Users[1].Id, (ret[15] as DeleteCascadeUser).Id);
 | 
			
		||||
            Assert.Equal(group.Users[1].Username, (ret[15] as DeleteCascadeUser).Username);
 | 
			
		||||
            Assert.Equal(group.Users[1].Password, (ret[15] as DeleteCascadeUser).Password);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUser>(ret[16]);
 | 
			
		||||
            Assert.Equal(group.Users[2].Id, (ret[16] as DeleteCascadeUser).Id);
 | 
			
		||||
            Assert.Equal(group.Users[2].Username, (ret[16] as DeleteCascadeUser).Username);
 | 
			
		||||
            Assert.Equal(group.Users[2].Password, (ret[16] as DeleteCascadeUser).Password);
 | 
			
		||||
            Assert.IsType<DeleteCascadeUserGroup>(ret[17]);
 | 
			
		||||
            Assert.Equal(group.Id, (ret[17] as DeleteCascadeUserGroup).Id);
 | 
			
		||||
            Assert.Equal(group.GroupName, (ret[17] as DeleteCascadeUserGroup).GroupName);
 | 
			
		||||
        }
 | 
			
		||||
        public class DeleteCascadeUser
 | 
			
		||||
        {
 | 
			
		||||
            [Column(IsIdentity = true)]
 | 
			
		||||
            public int Id { get; set; }
 | 
			
		||||
            public string Username { get; set; }
 | 
			
		||||
            public string Password { get; set; }
 | 
			
		||||
            public int GroupId { get; set; }
 | 
			
		||||
 | 
			
		||||
            [Navigate(nameof(Id))]
 | 
			
		||||
            public DeleteCascadeUserExt UserExt { get; set; }
 | 
			
		||||
            [Navigate(ManyToMany = typeof(DeleteCascadeUserTag))]
 | 
			
		||||
            public List<DeleteCascadeTag> Tags { get; set; }
 | 
			
		||||
        }
 | 
			
		||||
        public class DeleteCascadeUserExt
 | 
			
		||||
        {
 | 
			
		||||
            [Column(IsPrimary = true)]
 | 
			
		||||
            public int UserId { get; set; }
 | 
			
		||||
            public string Remark { get; set; }
 | 
			
		||||
 | 
			
		||||
            [Navigate(nameof(UserId))]
 | 
			
		||||
            public DeleteCascadeUser User { get; set; }
 | 
			
		||||
        }
 | 
			
		||||
        public class DeleteCascadeUserGroup
 | 
			
		||||
        {
 | 
			
		||||
            [Column(IsIdentity = true)]
 | 
			
		||||
            public int Id { get; set; }
 | 
			
		||||
            public string GroupName { get; set; }
 | 
			
		||||
 | 
			
		||||
            [Navigate(nameof(DeleteCascadeUser.GroupId))]
 | 
			
		||||
            public List<DeleteCascadeUser> Users { get; set; }
 | 
			
		||||
        }
 | 
			
		||||
        public class DeleteCascadeTag
 | 
			
		||||
        {
 | 
			
		||||
            [Column(IsIdentity = true)]
 | 
			
		||||
            public int Id { get; set; }
 | 
			
		||||
            public string TagName { get; set; }
 | 
			
		||||
 | 
			
		||||
            [Navigate(ManyToMany = typeof(DeleteCascadeUserTag))]
 | 
			
		||||
            public List<DeleteCascadeUser> Users { get; set; }
 | 
			
		||||
        }
 | 
			
		||||
        public class DeleteCascadeUserTag
 | 
			
		||||
        {
 | 
			
		||||
            public int UserId { get; set; }
 | 
			
		||||
            public int TagId { get; set; }
 | 
			
		||||
 | 
			
		||||
            [Navigate(nameof(UserId))]
 | 
			
		||||
            public DeleteCascadeUser User { get; set; }
 | 
			
		||||
            [Navigate(nameof(TagId))]
 | 
			
		||||
            public DeleteCascadeTag Tag { get; set; }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 更一条无法更新。
 | 
			
		||||
        /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -148,7 +148,7 @@ UNION ALL
 | 
			
		||||
SELECT  * from (SELECT a.""id"", a.""msg"", a.""createtime""
 | 
			
		||||
FROM ""as_table_log_202201"" a
 | 
			
		||||
WHERE (a.""createtime"" < '2022-05-01 00:00:00')) ftb", dict);
 | 
			
		||||
            Assert.Equal(1, dict.Count);
 | 
			
		||||
            Assert.Single(dict);
 | 
			
		||||
            sql3 = sql2;
 | 
			
		||||
            dict.Select(a => sql3 = sql3.Replace(a.Key, "{0}".FormatMySql(a.Value))).ToList();
 | 
			
		||||
            Assert.Equal(sql1, sql3);
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ using FreeSql.Internal.Model;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Linq.Expressions;
 | 
			
		||||
using System.Reflection;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using Xunit;
 | 
			
		||||
@@ -109,120 +110,3 @@ namespace FreeSql.Tests.Sqlite
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public static class DeleteExtensions
 | 
			
		||||
{
 | 
			
		||||
    public static string ToSqlCascade<T>(this IDelete<T> that)
 | 
			
		||||
    {
 | 
			
		||||
        var delete = that as DeleteProvider;
 | 
			
		||||
        if (delete == null) return null;
 | 
			
		||||
        if (delete._whereTimes <= 0 || delete._where.Length == 0) return null;
 | 
			
		||||
        if (LocalGetNavigates(delete._table).Any() == false) return that.ToSql();
 | 
			
		||||
 | 
			
		||||
        var fsql = delete._orm;
 | 
			
		||||
        var sb = new StringBuilder();
 | 
			
		||||
        Dictionary<string, bool> eachdic = new Dictionary<string, bool>();
 | 
			
		||||
 | 
			
		||||
        var rootSel = fsql.Select<object>().AsType(delete._table.Type).Where(delete._where.ToString());
 | 
			
		||||
        var rootItems = rootSel.ToList();
 | 
			
		||||
        LocalEach(delete._table.Type, rootItems, true);
 | 
			
		||||
        return sb.ToString();
 | 
			
		||||
 | 
			
		||||
        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();
 | 
			
		||||
        }
 | 
			
		||||
        void LocalEach(Type itemType, List<object> items, bool isOneToOne)
 | 
			
		||||
        {
 | 
			
		||||
            items = items?.Where(item =>
 | 
			
		||||
            {
 | 
			
		||||
                var itemKeystr = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetEntityKeyString(fsql, itemType, item, false);
 | 
			
		||||
                var eachdicKey = $"{itemType.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(itemType);
 | 
			
		||||
            var navs = LocalGetNavigates(tb);
 | 
			
		||||
 | 
			
		||||
            var otos = navs.Where(a => a.Item1.RefType == TableRefType.OneToOne).ToList();
 | 
			
		||||
            if (otos.Any())
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var oto in otos)
 | 
			
		||||
                {
 | 
			
		||||
                    var childsSel = fsql.Select<object>().AsType(oto.Item1.RefEntityType) as Select1Provider<object>;
 | 
			
		||||
                    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(childsSel._tables[0].Table, refitem, oto.Item1.RefColumns[a].CsName, colval);
 | 
			
		||||
                        }
 | 
			
		||||
                        return refitem;
 | 
			
		||||
                    }).ToList();
 | 
			
		||||
 | 
			
		||||
                    childsSel.Where(childsSel._commonUtils.WhereItems(oto.Item1.RefColumns.ToArray(), "a.", refitems));
 | 
			
		||||
                    var childs = childsSel.ToList();
 | 
			
		||||
                    LocalEach(oto.Item1.RefEntityType, childs, false);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var otms = navs.Where(a => a.Item1.RefType == TableRefType.OneToMany).ToList();
 | 
			
		||||
            if (otms.Any())
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var otm in otms)
 | 
			
		||||
                {
 | 
			
		||||
                    var childsSel = fsql.Select<object>().AsType(otm.Item1.RefEntityType) as Select1Provider<object>;
 | 
			
		||||
                    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(childsSel._tables[0].Table, refitem, otm.Item1.RefColumns[a].CsName, colval);
 | 
			
		||||
                        }
 | 
			
		||||
                        return refitem;
 | 
			
		||||
                    }).ToList();
 | 
			
		||||
 | 
			
		||||
                    childsSel.Where(childsSel._commonUtils.WhereItems(otm.Item1.RefColumns.ToArray(), "a.", refitems));
 | 
			
		||||
                    var childs = childsSel.ToList();
 | 
			
		||||
                    LocalEach(otm.Item1.RefEntityType, childs, true);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var mtms = navs.Where(a => a.Item1.RefType == TableRefType.ManyToMany).ToList();
 | 
			
		||||
            if (mtms.Any())
 | 
			
		||||
            {
 | 
			
		||||
                foreach (var mtm in mtms)
 | 
			
		||||
                {
 | 
			
		||||
                    var childsSel = fsql.Select<object>().AsType(mtm.Item1.RefMiddleEntityType) as Select1Provider<object>;
 | 
			
		||||
                    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(childsSel._tables[0].Table, refitem, mtm.Item1.MiddleColumns[a].CsName, colval);
 | 
			
		||||
                        }
 | 
			
		||||
                        return refitem;
 | 
			
		||||
                    }).ToList();
 | 
			
		||||
 | 
			
		||||
                    childsSel.Where(childsSel._commonUtils.WhereItems(mtm.Item1.MiddleColumns.Take(mtm.Item1.Columns.Count).ToArray(), "a.", miditems));
 | 
			
		||||
                    var childs = childsSel.ToList();
 | 
			
		||||
                    LocalEach(mtm.Item1.RefEntityType, childs, true);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            var delSql = fsql.Delete<object>().AsType(itemType).WhereDynamic(items).ToSql();
 | 
			
		||||
            if (string.IsNullOrWhiteSpace(delSql)) throw new Exception($"ToSqlCascade 失败");
 | 
			
		||||
            if (sb.Length > 0) sb.Append("\r\n\r\n;\r\n\r\n");
 | 
			
		||||
            sb.Append(delSql);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -373,8 +373,6 @@ WHERE (((a.""Name"") in (SELECT s.""Title"" as1
 | 
			
		||||
            var ddd = g.sqlite.Select<District>().LeftJoin(d => d.ParentCode == d.Parent.Code).ToTreeList();
 | 
			
		||||
            Assert.Single(ddd);
 | 
			
		||||
            Assert.Equal(2, ddd[0].Childs.Count);
 | 
			
		||||
 | 
			
		||||
            var sql = g.sqlite.Delete<District>().Where(a => a.Code == "001").ToSqlCascade();
 | 
			
		||||
        }
 | 
			
		||||
        public class District
 | 
			
		||||
        {
 | 
			
		||||
@@ -2238,8 +2236,6 @@ WHERE (((cast(a.""Id"" as character)) in (SELECT b.""Title""
 | 
			
		||||
                })
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            var sql = g.sqlite.Delete<VM_District_Child>().Where(a => a.Code == "100000").ToSqlCascade();
 | 
			
		||||
 | 
			
		||||
            var t1 = fsql.Select<VM_District_Parent>()
 | 
			
		||||
                .InnerJoin(a => a.ParentCode == a.Parent.Code)
 | 
			
		||||
                .Where(a => a.Code == "110101")
 | 
			
		||||
 
 | 
			
		||||
@@ -56,7 +56,7 @@ namespace FreeSql.SqlServer.Curd
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    exception = ex;
 | 
			
		||||
                    throw ex;
 | 
			
		||||
                    throw;
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
@@ -113,7 +113,7 @@ namespace FreeSql.SqlServer.Curd
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    exception = ex;
 | 
			
		||||
                    throw ex;
 | 
			
		||||
                    throw;
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
 
 | 
			
		||||
@@ -65,7 +65,7 @@ namespace FreeSql.SqlServer.Curd
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    exception = ex;
 | 
			
		||||
                    throw ex;
 | 
			
		||||
                    throw;
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
@@ -158,7 +158,7 @@ namespace FreeSql.SqlServer.Curd
 | 
			
		||||
                catch (Exception ex)
 | 
			
		||||
                {
 | 
			
		||||
                    exception = ex;
 | 
			
		||||
                    throw ex;
 | 
			
		||||
                    throw;
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user