From 85e732fab0507cb03611547177470fedff8f5829 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Fri, 22 Nov 2024 11:23:32 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E6=81=A2=E5=A4=8D=20BaseEntity=20SaveMany?= =?UTF-8?q?=20=E6=96=B9=E6=B3=95=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AggregateRootRepositoryAsync.cs | 10 ++ .../BaseEntity.cs | 13 ++ .../BaseEntityAsync.cs | 13 ++ .../FreeSql.Extensions.BaseEntity.xml | 14 ++ FreeSql.DbContext/DbSet/DbSetAsync.cs | 59 +++++++ .../Repository/BaseRepositoryAsync.cs | 6 + .../Repository/Repository/IBaseRepository.cs | 1 + FreeSql/FreeSql.xml | 153 ++++-------------- 8 files changed, 146 insertions(+), 123 deletions(-) diff --git a/Extensions/FreeSql.Extensions.AggregateRoot/AggregateRootRepository/AggregateRootRepositoryAsync.cs b/Extensions/FreeSql.Extensions.AggregateRoot/AggregateRootRepository/AggregateRootRepositoryAsync.cs index b84d2548f..d01d1f7de 100644 --- a/Extensions/FreeSql.Extensions.AggregateRoot/AggregateRootRepository/AggregateRootRepositoryAsync.cs +++ b/Extensions/FreeSql.Extensions.AggregateRoot/AggregateRootRepository/AggregateRootRepositoryAsync.cs @@ -226,6 +226,16 @@ namespace FreeSql return affrows; } + async public virtual Task SaveManyAsync(TEntity entity, string propertyName, CancellationToken cancellationToken = default) + { + var tracking = new AggregateRootTrackingChangeInfo(); + var stateKey = Orm.GetEntityKeyString(EntityType, entity, false); + if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以保存数据 {Orm.GetEntityString(EntityType, entity)}"); + AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, state.Value, entity, propertyName, tracking); + await SaveTrackingChangeAsync(tracking, cancellationToken); + Attach(entity); //应该只存储 propertyName 内容 + } + async Task SaveTrackingChangeAsync(AggregateRootTrackingChangeInfo tracking, CancellationToken cancellationToken) { var affrows = 0; diff --git a/Extensions/FreeSql.Extensions.BaseEntity/BaseEntity.cs b/Extensions/FreeSql.Extensions.BaseEntity/BaseEntity.cs index 5deee117e..24f347281 100644 --- a/Extensions/FreeSql.Extensions.BaseEntity/BaseEntity.cs +++ b/Extensions/FreeSql.Extensions.BaseEntity/BaseEntity.cs @@ -162,5 +162,18 @@ namespace FreeSql Repository.UnitOfWork = _resolveUow?.Invoke(); return Repository.InsertOrUpdate(this as TEntity); } + + /// + /// To completely save the navigation properties of the entity in the form of sub-tables.
+ /// 【完整】保存导航属性,子表 + ///
+ /// Navigation property name + public virtual void SaveMany(string navigatePropertyName) + { + if (Repository == null) + Repository = Orm.GetRepository(); + Repository.UnitOfWork = _resolveUow?.Invoke(); + Repository.SaveMany(this as TEntity, navigatePropertyName); + } } } \ No newline at end of file diff --git a/Extensions/FreeSql.Extensions.BaseEntity/BaseEntityAsync.cs b/Extensions/FreeSql.Extensions.BaseEntity/BaseEntityAsync.cs index 908288189..da57e2036 100644 --- a/Extensions/FreeSql.Extensions.BaseEntity/BaseEntityAsync.cs +++ b/Extensions/FreeSql.Extensions.BaseEntity/BaseEntityAsync.cs @@ -149,6 +149,19 @@ namespace FreeSql Repository.UnitOfWork = _resolveUow?.Invoke(); return Repository.InsertOrUpdateAsync(this as TEntity); } + + /// + /// To completely save the navigation properties of the entity in the form of sub-tables.
+ /// 【完整】保存导航属性,子表 + ///
+ /// Navigation property name + public virtual Task SaveManyAsync(string navigatePropertyName) + { + if (Repository == null) + Repository = Orm.GetRepository(); + Repository.UnitOfWork = _resolveUow?.Invoke(); + return Repository.SaveManyAsync(this as TEntity, navigatePropertyName); + } #endif } } \ No newline at end of file diff --git a/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.xml b/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.xml index 1508d48fe..f5d4630c1 100644 --- a/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.xml +++ b/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.xml @@ -82,6 +82,13 @@ + + + To completely save the navigation properties of the entity in the form of sub-tables.
+ 【完整】保存导航属性,子表 +
+ Navigation property name +
Entity base class, including CreateTime/UpdateTime/IsDeleted, the async CRUD methods, and ID primary key definition. @@ -152,6 +159,13 @@ + + + To completely save the navigation properties of the entity in the form of sub-tables.
+ 【完整】保存导航属性,子表 +
+ Navigation property name +
Entity base class, including CreateTime/UpdateTime/IsDeleted. diff --git a/FreeSql.DbContext/DbSet/DbSetAsync.cs b/FreeSql.DbContext/DbSet/DbSetAsync.cs index 361bd4473..4db2fbe4f 100644 --- a/FreeSql.DbContext/DbSet/DbSetAsync.cs +++ b/FreeSql.DbContext/DbSet/DbSetAsync.cs @@ -149,6 +149,65 @@ namespace FreeSql await AddOrUpdateNavigateAsync(item, true, null, cancellationToken); } + async public Task SaveManyAsync(TEntity item, string propertyName, CancellationToken cancellationToken = default) + { + if (item == null) return; + if (string.IsNullOrEmpty(propertyName)) return; + if (_table.Properties.TryGetValue(propertyName, out var prop) == false) throw new KeyNotFoundException(DbContextErrorStrings.NotFound_Property(_table.Type.FullName, propertyName)); + if (_table.ColumnsByCsIgnore.ContainsKey(propertyName)) throw new ArgumentException(DbContextErrorStrings.TypeHasSetProperty_IgnoreAttribute(_table.Type.FullName, propertyName)); + + var tref = _table.GetTableRef(propertyName, true, false); + if (tref == null) return; + switch (tref.RefType) + { + case TableRefType.OneToOne: + case TableRefType.ManyToOne: + case TableRefType.PgArrayToMany: + throw new ArgumentException(DbContextErrorStrings.PropertyOfType_IsNot_OneToManyOrManyToMany(_table.Type.FullName, propertyName)); + } + + await DbContextFlushCommandAsync(cancellationToken); + var oldEnable = _db.Options.EnableCascadeSave; + _db.Options.EnableCascadeSave = false; + try + { + await AddOrUpdateNavigateAsync(item, false, propertyName, cancellationToken); + if (tref.RefType == TableRefType.OneToMany) + { + await DbContextFlushCommandAsync(cancellationToken); + //删除没有保存的数据,求出主体的条件 + var deleteWhereParentParam = Expression.Parameter(typeof(object), "a"); + Expression whereParentExp = null; + for (var colidx = 0; colidx < tref.Columns.Count; colidx++) + { + var whereExp = Expression.Equal( + Expression.MakeMemberAccess(Expression.Convert(deleteWhereParentParam, tref.RefEntityType), tref.RefColumns[colidx].Table.Properties[tref.RefColumns[colidx].CsName]), + Expression.Constant( + FreeSql.Internal.Utils.GetDataReaderValue( + tref.Columns[colidx].CsType, + _db.OrmOriginal.GetEntityValueWithPropertyName(_table.Type, item, tref.Columns[colidx].CsName)), tref.RefColumns[colidx].CsType) + ); + if (whereParentExp == null) whereParentExp = whereExp; + else whereParentExp = Expression.AndAlso(whereParentExp, whereExp); + } + var propValEach = GetItemValue(item, prop) as IEnumerable; + var subDelete = _db.OrmOriginal.Delete().AsType(tref.RefEntityType) + .WithTransaction(_uow?.GetOrBeginTransaction()) + .Where(Expression.Lambda>(whereParentExp, deleteWhereParentParam)); + foreach (var propValItem in propValEach) + { + subDelete.WhereDynamic(propValEach, true); + break; + } + await subDelete.ExecuteAffrowsAsync(cancellationToken); + } + } + finally + { + _db.Options.EnableCascadeSave = oldEnable; + } + } + async Task AddOrUpdateNavigateAsync(TEntity item, bool isAdd, string propertyName, CancellationToken cancellationToken) { Func action = async prop => diff --git a/FreeSql.DbContext/Repository/Repository/BaseRepositoryAsync.cs b/FreeSql.DbContext/Repository/Repository/BaseRepositoryAsync.cs index 3612f3ec1..5ade3e001 100644 --- a/FreeSql.DbContext/Repository/Repository/BaseRepositoryAsync.cs +++ b/FreeSql.DbContext/Repository/Repository/BaseRepositoryAsync.cs @@ -68,6 +68,12 @@ namespace FreeSql await _db.SaveChangesAsync(cancellationToken); return entity; } + + public virtual async Task SaveManyAsync(TEntity entity, string propertyName, CancellationToken cancellationToken = default) + { + await _dbset.SaveManyAsync(entity, propertyName, cancellationToken); + await _db.SaveChangesAsync(cancellationToken); + } } partial class BaseRepository diff --git a/FreeSql.DbContext/Repository/Repository/IBaseRepository.cs b/FreeSql.DbContext/Repository/Repository/IBaseRepository.cs index 662a6ee10..4294081ee 100644 --- a/FreeSql.DbContext/Repository/Repository/IBaseRepository.cs +++ b/FreeSql.DbContext/Repository/Repository/IBaseRepository.cs @@ -126,6 +126,7 @@ namespace FreeSql Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default); Task UpdateAsync(IEnumerable entitys, CancellationToken cancellationToken = default); Task InsertOrUpdateAsync(TEntity entity, CancellationToken cancellationToken = default); + Task SaveManyAsync(TEntity entity, string propertyName, CancellationToken cancellationToken = default); Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default); Task DeleteAsync(IEnumerable entitys, CancellationToken cancellationToken = default); diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index fdde4800e..319d5d395 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -5831,6 +5831,36 @@ + + + field IN (value1) + + + + + field in (value1, value2) + + + + + field in (value1, value2, value3) + + + + + field in (value1, value2, value3, value4) + + + + + field in (value1, value2, value3, value4, value5) + + + + + field in (values) + + 获取 Type 的原始 c# 文本表示 @@ -6346,126 +6376,3 @@ - - - - - 插入数据,传入实体集合 - - - - - - - - 插入数据,传入实体集合 - - - - - - - - 插入或更新数据,此功能依赖数据库特性(低版本可能不支持),参考如下: - MySql 5.6+: on duplicate key update - PostgreSQL 9.4+: on conflict do update - SqlServer 2008+: merge into - Oracle 11+: merge into - Sqlite: replace into - DuckDB: on conflict do update - 达梦: merge into - 人大金仓:on conflict do update - 神通:merge into - MsAccess:不支持 - 注意区别:FreeSql.Repository 仓储也有 InsertOrUpdate 方法(不依赖数据库特性) - - - - - - - 修改数据 - - - - - - - 修改数据,传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - - - 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 - - - - - 查询数据 - - - - - - - 查询数据,传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - - - 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 - - - - - 删除数据 - - - - - - - 删除数据,传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - - - 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 - - - - - 开启事务(不支持异步) - v1.5.0 关闭了线程事务超时自动提交的机制 - - 事务体 () => {} - - - - 开启事务(不支持异步) - v1.5.0 关闭了线程事务超时自动提交的机制 - - - 事务体 () => {} - - - - 数据库访问对象 - - - - - 所有拦截方法都在这里 - - - - - CodeFirst 模式开发相关方法 - - - - - DbFirst 模式开发相关方法 - - - - - 全局过滤设置,可默认附加为 Select/Update/Delete 条件 - - - -