mirror of
				https://github.com/nsnail/FreeSql.git
				synced 2025-11-04 17:20:49 +08:00 
			
		
		
		
	- 增加 DbContext、Repository SaveManyToMany 方法,实现手工保存 ManyToMany 关联数据;
This commit is contained in:
		@@ -136,6 +136,13 @@ namespace FreeSql
 | 
			
		||||
        /// <param name="data"></param>
 | 
			
		||||
        public void AddOrUpdate<TEntity>(TEntity data) where TEntity : class => this.Set<TEntity>().AddOrUpdate(data);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 保存实体的指定 ManyToMany 导航属性
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="data">实体对象</param>
 | 
			
		||||
        /// <param name="propertyName">属性名</param>
 | 
			
		||||
        public void SaveManyToMany<TEntity>(TEntity data, string propertyName) where TEntity : class => this.Set<TEntity>().SaveManyToMany(data, propertyName);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 附加实体,可用于不查询就更新或删除
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@@ -163,6 +170,7 @@ namespace FreeSql
 | 
			
		||||
        public Task UpdateRangeAsync<TEntity>(IEnumerable<TEntity> data) where TEntity : class => this.Set<TEntity>().UpdateRangeAsync(data);
 | 
			
		||||
 | 
			
		||||
        public Task AddOrUpdateAsync<TEntity>(TEntity data) where TEntity : class => this.Set<TEntity>().AddOrUpdateAsync(data);
 | 
			
		||||
        public Task SaveManyToManyAsync<TEntity>(TEntity data, string propertyName) where TEntity : class => this.Set<TEntity>().SaveManyToManyAsync(data, propertyName);
 | 
			
		||||
#endif
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -132,44 +132,58 @@ namespace FreeSql
 | 
			
		||||
                        await AddOrUpdateNavigateListAsync(item, true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        async Task AddOrUpdateNavigateListAsync(TEntity item, bool isAdd)
 | 
			
		||||
 | 
			
		||||
        async public Task SaveManyToManyAsync(TEntity item, string propertyName)
 | 
			
		||||
        {
 | 
			
		||||
            if (item == null) return;
 | 
			
		||||
            if (_table.Properties.ContainsKey(propertyName) == false) throw new KeyNotFoundException($"{_table.Type.FullName} 不存在属性 {propertyName}");
 | 
			
		||||
            if (_table.ColumnsByCsIgnore.ContainsKey(propertyName)) throw new ArgumentException($"{_table.Type.FullName} 类型已设置属性 {propertyName} 忽略特性");
 | 
			
		||||
            var tref = _table.GetTableRef(propertyName, true);
 | 
			
		||||
            if (tref.RefType != Internal.Model.TableRefType.ManyToMany) throw new ArgumentException($"{_table.Type.FullName} 类型的属性 {propertyName} 不是 ManyToMany 特性");
 | 
			
		||||
            DbContextExecCommand();
 | 
			
		||||
            var oldEnable = _db.Options.EnableAddOrUpdateNavigateList;
 | 
			
		||||
            _db.Options.EnableAddOrUpdateNavigateList = false;
 | 
			
		||||
            await AddOrUpdateNavigateListAsync(item, false, propertyName);
 | 
			
		||||
            _db.Options.EnableAddOrUpdateNavigateList = oldEnable;
 | 
			
		||||
        }
 | 
			
		||||
        async Task AddOrUpdateNavigateListAsync(TEntity item, bool isAdd, string propertyName = null)
 | 
			
		||||
        {
 | 
			
		||||
            Type itemType = null;
 | 
			
		||||
            foreach (var prop in _table.Properties)
 | 
			
		||||
            Func<PropertyInfo, Task> action = async prop =>
 | 
			
		||||
            {
 | 
			
		||||
                if (_table.ColumnsByCsIgnore.ContainsKey(prop.Key)) continue;
 | 
			
		||||
                if (_table.ColumnsByCs.ContainsKey(prop.Key)) continue;
 | 
			
		||||
                if (_table.ColumnsByCsIgnore.ContainsKey(prop.Name)) return;
 | 
			
		||||
                if (_table.ColumnsByCs.ContainsKey(prop.Name)) return;
 | 
			
		||||
 | 
			
		||||
                var tref = _table.GetTableRef(prop.Key, true);
 | 
			
		||||
                if (tref == null) continue;
 | 
			
		||||
                var tref = _table.GetTableRef(prop.Name, true);
 | 
			
		||||
                if (tref == null) return;
 | 
			
		||||
                switch (tref.RefType)
 | 
			
		||||
                {
 | 
			
		||||
                    case Internal.Model.TableRefType.OneToOne:
 | 
			
		||||
                    case Internal.Model.TableRefType.ManyToOne:
 | 
			
		||||
                        continue;
 | 
			
		||||
                        return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                object propVal = null;
 | 
			
		||||
                if (itemType == null) itemType = item.GetType();
 | 
			
		||||
                if (_table.TypeLazy != null && itemType == _table.TypeLazy)
 | 
			
		||||
                {
 | 
			
		||||
                    var lazyField = _dicLazyIsSetField.GetOrAdd(_table.TypeLazy, tl => new ConcurrentDictionary<string, FieldInfo>()).GetOrAdd(prop.Key, propName =>
 | 
			
		||||
                    var lazyField = _dicLazyIsSetField.GetOrAdd(_table.TypeLazy, tl => new ConcurrentDictionary<string, FieldInfo>()).GetOrAdd(prop.Name, propName =>
 | 
			
		||||
                        _table.TypeLazy.GetField($"__lazy__{propName}", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance));
 | 
			
		||||
                    if (lazyField != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        var lazyFieldValue = (bool)lazyField.GetValue(item);
 | 
			
		||||
                        if (lazyFieldValue == false) continue;
 | 
			
		||||
                        if (lazyFieldValue == false) return;
 | 
			
		||||
                    }
 | 
			
		||||
                    propVal = prop.Value.GetValue(item);
 | 
			
		||||
                    propVal = prop.GetValue(item);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    propVal = prop.Value.GetValue(item);
 | 
			
		||||
                    if (propVal == null) continue;
 | 
			
		||||
                    propVal = prop.GetValue(item);
 | 
			
		||||
                    if (propVal == null) return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var propValEach = propVal as IEnumerable;
 | 
			
		||||
                if (propValEach == null) continue;
 | 
			
		||||
                if (propValEach == null) return;
 | 
			
		||||
                DbSet<object> refSet = GetDbSetObject(tref.RefEntityType);
 | 
			
		||||
                switch (tref.RefType)
 | 
			
		||||
                {
 | 
			
		||||
@@ -276,7 +290,13 @@ namespace FreeSql
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            if (string.IsNullOrEmpty(propertyName))
 | 
			
		||||
                foreach (var prop in _table.Properties)
 | 
			
		||||
                    await action(prop.Value);
 | 
			
		||||
            else if (_table.Properties.TryGetValue(propertyName, out var prop))
 | 
			
		||||
                await action(prop);
 | 
			
		||||
        }
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -135,50 +135,70 @@ namespace FreeSql
 | 
			
		||||
                        AddOrUpdateNavigateList(item, true);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        static ConcurrentDictionary<Type, ConcurrentDictionary<string, FieldInfo>> _dicLazyIsSetField = new ConcurrentDictionary<Type, ConcurrentDictionary<string, FieldInfo>>();
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 保存实体的指定 ManyToMany 导航属性
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="item">实体对象</param>
 | 
			
		||||
        /// <param name="propertyName">属性名</param>
 | 
			
		||||
        public void SaveManyToMany(TEntity item, string propertyName)
 | 
			
		||||
        {
 | 
			
		||||
            if (item == null) return;
 | 
			
		||||
            if (_table.Properties.ContainsKey(propertyName) == false) throw new KeyNotFoundException($"{_table.Type.FullName} 不存在属性 {propertyName}");
 | 
			
		||||
            if (_table.ColumnsByCsIgnore.ContainsKey(propertyName)) throw new ArgumentException($"{_table.Type.FullName} 类型已设置属性 {propertyName} 忽略特性");
 | 
			
		||||
            var tref = _table.GetTableRef(propertyName, true);
 | 
			
		||||
            if (tref.RefType != Internal.Model.TableRefType.ManyToMany) throw new ArgumentException($"{_table.Type.FullName} 类型的属性 {propertyName} 不是 ManyToMany 特性");
 | 
			
		||||
            DbContextExecCommand();
 | 
			
		||||
            var oldEnable = _db.Options.EnableAddOrUpdateNavigateList;
 | 
			
		||||
            _db.Options.EnableAddOrUpdateNavigateList = false;
 | 
			
		||||
            AddOrUpdateNavigateList(item, false, propertyName);
 | 
			
		||||
            _db.Options.EnableAddOrUpdateNavigateList = oldEnable;
 | 
			
		||||
        }
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 联级保存导航集合
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="item">实体对象</param>
 | 
			
		||||
        /// <param name="isAdd">是否为新增的实体对象</param>
 | 
			
		||||
        void AddOrUpdateNavigateList(TEntity item, bool isAdd)
 | 
			
		||||
        /// <param name="propertyName">指定保存的属性</param>
 | 
			
		||||
        void AddOrUpdateNavigateList(TEntity item, bool isAdd, string propertyName = null)
 | 
			
		||||
        {
 | 
			
		||||
            Type itemType = null;
 | 
			
		||||
            foreach (var prop in _table.Properties)
 | 
			
		||||
            Action<PropertyInfo> action = prop =>
 | 
			
		||||
            {
 | 
			
		||||
                if (_table.ColumnsByCsIgnore.ContainsKey(prop.Key)) continue;
 | 
			
		||||
                if (_table.ColumnsByCs.ContainsKey(prop.Key)) continue;
 | 
			
		||||
                if (_table.ColumnsByCsIgnore.ContainsKey(prop.Name)) return;
 | 
			
		||||
                if (_table.ColumnsByCs.ContainsKey(prop.Name)) return;
 | 
			
		||||
 | 
			
		||||
                var tref = _table.GetTableRef(prop.Key, true);
 | 
			
		||||
                if (tref == null) continue;
 | 
			
		||||
                var tref = _table.GetTableRef(prop.Name, true);
 | 
			
		||||
                if (tref == null) return;
 | 
			
		||||
                switch (tref.RefType)
 | 
			
		||||
                {
 | 
			
		||||
                    case Internal.Model.TableRefType.OneToOne:
 | 
			
		||||
                    case Internal.Model.TableRefType.ManyToOne:
 | 
			
		||||
                        continue;
 | 
			
		||||
                        return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                object propVal = null;
 | 
			
		||||
                if (itemType == null) itemType = item.GetType();
 | 
			
		||||
                if (_table.TypeLazy != null && itemType == _table.TypeLazy)
 | 
			
		||||
                {
 | 
			
		||||
                    var lazyField = _dicLazyIsSetField.GetOrAdd(_table.TypeLazy, tl => new ConcurrentDictionary<string, FieldInfo>()).GetOrAdd(prop.Key, propName =>
 | 
			
		||||
                    var lazyField = _dicLazyIsSetField.GetOrAdd(_table.TypeLazy, tl => new ConcurrentDictionary<string, FieldInfo>()).GetOrAdd(prop.Name, propName =>
 | 
			
		||||
                        _table.TypeLazy.GetField($"__lazy__{propName}", BindingFlags.GetField | BindingFlags.NonPublic | BindingFlags.Instance));
 | 
			
		||||
                    if (lazyField != null)
 | 
			
		||||
                    {
 | 
			
		||||
                        var lazyFieldValue = (bool)lazyField.GetValue(item);
 | 
			
		||||
                        if (lazyFieldValue == false) continue;
 | 
			
		||||
                        if (lazyFieldValue == false) return;
 | 
			
		||||
                    }
 | 
			
		||||
                    propVal = prop.Value.GetValue(item, null);
 | 
			
		||||
                    propVal = prop.GetValue(item, null);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    propVal = prop.Value.GetValue(item, null);
 | 
			
		||||
                    if (propVal == null) continue;
 | 
			
		||||
                    propVal = prop.GetValue(item, null);
 | 
			
		||||
                    if (propVal == null) return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var propValEach = propVal as IEnumerable;
 | 
			
		||||
                if (propValEach == null) continue;
 | 
			
		||||
                if (propValEach == null) return;
 | 
			
		||||
                DbSet<object> refSet = GetDbSetObject(tref.RefEntityType);
 | 
			
		||||
                switch (tref.RefType)
 | 
			
		||||
                {
 | 
			
		||||
@@ -230,7 +250,7 @@ namespace FreeSql
 | 
			
		||||
                            foreach (var midItem in midList)
 | 
			
		||||
                            {
 | 
			
		||||
                                var curContains = new List<int>();
 | 
			
		||||
                                for(var curIdx = 0; curIdx < curList.Count; curIdx ++)
 | 
			
		||||
                                for (var curIdx = 0; curIdx < curList.Count; curIdx++)
 | 
			
		||||
                                {
 | 
			
		||||
                                    var isEquals = true;
 | 
			
		||||
                                    for (var midcolidx = tref.Columns.Count; midcolidx < tref.MiddleColumns.Count; midcolidx++)
 | 
			
		||||
@@ -285,7 +305,13 @@ namespace FreeSql
 | 
			
		||||
                        }
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            if (string.IsNullOrEmpty(propertyName))
 | 
			
		||||
                foreach (var prop in _table.Properties)
 | 
			
		||||
                    action(prop.Value);
 | 
			
		||||
            else if (_table.Properties.TryGetValue(propertyName, out var prop))
 | 
			
		||||
                action(prop);
 | 
			
		||||
        }
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,13 @@
 | 
			
		||||
            <typeparam name="TEntity"></typeparam>
 | 
			
		||||
            <param name="data"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:FreeSql.DbContext.SaveManyToMany``1(``0,System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            保存实体的指定 ManyToMany 导航属性
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="data">实体对象</param>
 | 
			
		||||
            <param name="propertyName">属性名</param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:FreeSql.DbContext.Attach``1(``0)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            附加实体,可用于不查询就更新或删除
 | 
			
		||||
@@ -99,25 +106,26 @@
 | 
			
		||||
            清空状态数据
 | 
			
		||||
            </summary>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:FreeSql.DbSet`1.RemoveAsync(System.Linq.Expressions.Expression{System.Func{`0,System.Boolean}})">
 | 
			
		||||
            <summary>
 | 
			
		||||
            根据 lambda 条件删除数据
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="predicate"></param>
 | 
			
		||||
            <returns></returns>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:FreeSql.DbSet`1.Add(`0)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            添加
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="data"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:FreeSql.DbSet`1.AddOrUpdateNavigateList(`0,System.Boolean)">
 | 
			
		||||
        <member name="M:FreeSql.DbSet`1.SaveManyToMany(`0,System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            保存实体的指定 ManyToMany 导航属性
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="item">实体对象</param>
 | 
			
		||||
            <param name="propertyName">属性名</param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:FreeSql.DbSet`1.AddOrUpdateNavigateList(`0,System.Boolean,System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            联级保存导航集合
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="item">实体对象</param>
 | 
			
		||||
            <param name="isAdd">是否为新增的实体对象</param>
 | 
			
		||||
            <param name="propertyName">指定保存的属性</param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:FreeSql.DbSet`1.Update(`0)">
 | 
			
		||||
            <summary>
 | 
			
		||||
@@ -225,6 +233,13 @@
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="data"></param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="M:FreeSql.IBasicRepository`1.SaveManyToMany(`0,System.String)">
 | 
			
		||||
            <summary>
 | 
			
		||||
            保存实体的指定 ManyToMany 导航属性
 | 
			
		||||
            </summary>
 | 
			
		||||
            <param name="entity">实体对象</param>
 | 
			
		||||
            <param name="propertyName">属性名</param>
 | 
			
		||||
        </member>
 | 
			
		||||
        <member name="P:FreeSql.IUnitOfWork.Enable">
 | 
			
		||||
            <summary>
 | 
			
		||||
            是否启用工作单元
 | 
			
		||||
 
 | 
			
		||||
@@ -129,6 +129,12 @@ namespace FreeSql
 | 
			
		||||
            _db.SaveChanges();
 | 
			
		||||
            return entity;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void SaveManyToMany(TEntity entity, string propertyName)
 | 
			
		||||
        {
 | 
			
		||||
            _dbset.SaveManyToMany(entity, propertyName);
 | 
			
		||||
            _db.SaveChanges();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public abstract partial class BaseRepository<TEntity, TKey> : BaseRepository<TEntity>, IBaseRepository<TEntity, TKey>
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,7 @@ namespace FreeSql
 | 
			
		||||
        async public virtual Task<TEntity> InsertAsync(TEntity entity)
 | 
			
		||||
        {
 | 
			
		||||
            await _dbset.AddAsync(entity);
 | 
			
		||||
            _db.SaveChanges();
 | 
			
		||||
            await _db.SaveChangesAsync();
 | 
			
		||||
            return entity;
 | 
			
		||||
        }
 | 
			
		||||
        async public virtual Task<List<TEntity>> InsertAsync(IEnumerable<TEntity> entitys)
 | 
			
		||||
@@ -60,9 +60,15 @@ namespace FreeSql
 | 
			
		||||
        async public Task<TEntity> InsertOrUpdateAsync(TEntity entity)
 | 
			
		||||
        {
 | 
			
		||||
            await _dbset.AddOrUpdateAsync(entity);
 | 
			
		||||
            _db.SaveChanges();
 | 
			
		||||
            await _db.SaveChangesAsync();
 | 
			
		||||
            return entity;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        async public Task SaveManyToManyAsync(TEntity entity, string propertyName)
 | 
			
		||||
        {
 | 
			
		||||
            await _dbset.SaveManyToManyAsync(entity, propertyName);
 | 
			
		||||
            await _db.SaveChangesAsync();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    partial class BaseRepository<TEntity, TKey>
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,12 @@ namespace FreeSql
 | 
			
		||||
        int Update(IEnumerable<TEntity> entitys);
 | 
			
		||||
 | 
			
		||||
        TEntity InsertOrUpdate(TEntity entity);
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// 保存实体的指定 ManyToMany 导航属性
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="entity">实体对象</param>
 | 
			
		||||
        /// <param name="propertyName">属性名</param>
 | 
			
		||||
        void SaveManyToMany(TEntity entity, string propertyName);
 | 
			
		||||
 | 
			
		||||
        IUpdate<TEntity> UpdateDiy { get; }
 | 
			
		||||
 | 
			
		||||
@@ -43,6 +49,7 @@ namespace FreeSql
 | 
			
		||||
        Task<int> UpdateAsync(TEntity entity);
 | 
			
		||||
        Task<int> UpdateAsync(IEnumerable<TEntity> entitys);
 | 
			
		||||
        Task<TEntity> InsertOrUpdateAsync(TEntity entity);
 | 
			
		||||
        Task SaveManyToManyAsync(TEntity entity, string propertyName);
 | 
			
		||||
 | 
			
		||||
        Task<int> DeleteAsync(TEntity entity);
 | 
			
		||||
        Task<int> DeleteAsync(IEnumerable<TEntity> entitys);
 | 
			
		||||
 
 | 
			
		||||
@@ -415,7 +415,9 @@ namespace FreeSql.Tests
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
            var repo = g.sqlite.GetRepository<Song>();
 | 
			
		||||
            //repo.DbContextOptions.EnableAddOrUpdateNavigateList = false; //<2F>ر<EFBFBD><D8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>湦<EFBFBD><E6B9A6>
 | 
			
		||||
            repo.Insert(ss);
 | 
			
		||||
            repo.SaveManyToMany(ss[0], "Tags"); //ָ<><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD> Tags <20><><EFBFBD>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD>
 | 
			
		||||
 | 
			
		||||
            ss[0].Name = "<22><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>.mp5";
 | 
			
		||||
            ss[0].Tags.Clear();
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user