diff --git a/Directory.Build.props b/Directory.Build.props index b648c47c..df4b1f61 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -11,7 +11,7 @@ diff --git a/Extensions/FreeSql.Extensions.AggregateRoot/FreeSql.Extensions.AggregateRoot.csproj b/Extensions/FreeSql.Extensions.AggregateRoot/FreeSql.Extensions.AggregateRoot.csproj index 6c145249..492b0057 100644 --- a/Extensions/FreeSql.Extensions.AggregateRoot/FreeSql.Extensions.AggregateRoot.csproj +++ b/Extensions/FreeSql.Extensions.AggregateRoot/FreeSql.Extensions.AggregateRoot.csproj @@ -31,7 +31,11 @@ - + + + + net40 + diff --git a/Extensions/FreeSql.Extensions.AggregateRoot/FreeSql.Extensions.AggregateRoot.xml b/Extensions/FreeSql.Extensions.AggregateRoot/FreeSql.Extensions.AggregateRoot.xml index 75b8c707..eb26af16 100644 --- a/Extensions/FreeSql.Extensions.AggregateRoot/FreeSql.Extensions.AggregateRoot.xml +++ b/Extensions/FreeSql.Extensions.AggregateRoot/FreeSql.Extensions.AggregateRoot.xml @@ -1,21 +1,52 @@ - FreeSql.Extensions.JsonMap + FreeSql.Extensions.AggregateRoot - + - When the entity class property is , map storage in JSON format.
- 当实体类属性为【对象】时,以 JSON 形式映射存储 + 设置 AggregateRootRepository 边界范围 + 在边界范围之内的规则 : + 1、OneToOne/OneToMany/ManyToMany(中间表) 可以查询、可以增删改 + 2、ManyToOne/ManyToMany外部表/PgArrayToMany 只可以查询,不支持增删改(会被忽略)
- + - When the entity class property is and the attribute is marked as , map storage in JSON format.
- 当实体类属性为【对象】时,并且标记特性 [JsonMap] 时,该属性将以JSON形式映射存储 + 边界是否终止 +
+
+ + + 边界是否终止向下探测 + + + + + 默认:创建查询对象(递归包含 Include/IncludeMany 边界之内的导航属性) + 重写:使用 + + + + + 创建查询对象(纯净) + _ + 聚合根内关系较复杂时,获取 Include/IncludeMany 字符串代码,方便二次开发 + string code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(null, fsql, typeof(Order)) + + + + + 创建查询对象(递归包含 Include/IncludeMany 边界之内的导航属性) + + + ISelect.TrackToList 委托,数据返回后自动 Attach + + +
diff --git a/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.csproj b/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.csproj index dd7c1c20..14c832bc 100644 --- a/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.csproj +++ b/Extensions/FreeSql.Extensions.BaseEntity/FreeSql.Extensions.BaseEntity.csproj @@ -19,7 +19,7 @@ key.snk false latest - 3.2.667 + 3.2.668 diff --git a/Extensions/FreeSql.Extensions.JsonMap/FreeSql.Extensions.JsonMap.csproj b/Extensions/FreeSql.Extensions.JsonMap/FreeSql.Extensions.JsonMap.csproj index 6d1ebab7..e4ae462d 100644 --- a/Extensions/FreeSql.Extensions.JsonMap/FreeSql.Extensions.JsonMap.csproj +++ b/Extensions/FreeSql.Extensions.JsonMap/FreeSql.Extensions.JsonMap.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/Extensions/FreeSql.Extensions.LazyLoading/FreeSql.Extensions.LazyLoading.csproj b/Extensions/FreeSql.Extensions.LazyLoading/FreeSql.Extensions.LazyLoading.csproj index 157117c1..532df3f2 100644 --- a/Extensions/FreeSql.Extensions.LazyLoading/FreeSql.Extensions.LazyLoading.csproj +++ b/Extensions/FreeSql.Extensions.LazyLoading/FreeSql.Extensions.LazyLoading.csproj @@ -15,7 +15,7 @@ $(AssemblyName) true true - 3.2.667 + 3.2.668 diff --git a/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.csproj b/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.csproj index 21979675..fc9acdbf 100644 --- a/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.csproj +++ b/Extensions/FreeSql.Extensions.Linq/FreeSql.Extensions.Linq.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/Extensions/FreeSql.Generator/FreeSql.Generator.csproj b/Extensions/FreeSql.Generator/FreeSql.Generator.csproj index f20e00e0..0b93164f 100644 --- a/Extensions/FreeSql.Generator/FreeSql.Generator.csproj +++ b/Extensions/FreeSql.Generator/FreeSql.Generator.csproj @@ -13,7 +13,7 @@ https://github.com/2881099/FreeSql https://github.com/2881099/FreeSql FreeSql DbFirst 实体生成器 - 3.2.667 + 3.2.668 diff --git a/FreeSql-DbContext.sln b/FreeSql-DbContext.sln index c98618ed..b1b0fe45 100644 --- a/FreeSql-DbContext.sln +++ b/FreeSql-DbContext.sln @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Provider.Sqlite", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Tests.DbContext2", "FreeSql.Tests\FreeSql.Tests.DbContext2\FreeSql.Tests.DbContext2.csproj", "{5B0AFA8E-D367-4D30-85C0-107DACB0FF49}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FreeSql.Extensions.AggregateRoot", "Extensions\FreeSql.Extensions.AggregateRoot\FreeSql.Extensions.AggregateRoot.csproj", "{B8F84E4F-46F2-4048-B79B-49F0B8A95335}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ Global {5B0AFA8E-D367-4D30-85C0-107DACB0FF49}.Debug|Any CPU.Build.0 = Debug|Any CPU {5B0AFA8E-D367-4D30-85C0-107DACB0FF49}.Release|Any CPU.ActiveCfg = Release|Any CPU {5B0AFA8E-D367-4D30-85C0-107DACB0FF49}.Release|Any CPU.Build.0 = Release|Any CPU + {B8F84E4F-46F2-4048-B79B-49F0B8A95335}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B8F84E4F-46F2-4048-B79B-49F0B8A95335}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B8F84E4F-46F2-4048-B79B-49F0B8A95335}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B8F84E4F-46F2-4048-B79B-49F0B8A95335}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/FreeSql.All/FreeSql.All.csproj b/FreeSql.All/FreeSql.All.csproj index 0e34a197..3d40a7b1 100644 --- a/FreeSql.All/FreeSql.All.csproj +++ b/FreeSql.All/FreeSql.All.csproj @@ -17,7 +17,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/FreeSql.DbContext/AggregateRootRepository/AggregateRootBoundaryAttribute.cs b/FreeSql.DbContext/AggregateRootRepository/AggregateRootBoundaryAttribute.cs deleted file mode 100644 index a2603b44..00000000 --- a/FreeSql.DbContext/AggregateRootRepository/AggregateRootBoundaryAttribute.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Linq; - -namespace FreeSql.DataAnnotations -{ - - /// - /// 设置 AggregateRootRepository 边界范围 - /// 在边界范围之内的规则 : - /// 1、OneToOne/OneToMany/ManyToMany(中间表) 可以查询、可以增删改 - /// 2、ManyToOne/ManyToMany外部表/PgArrayToMany 只可以查询,不支持增删改(会被忽略) - /// - [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] - public class AggregateRootBoundaryAttribute : Attribute - { - public string Name { get; set; } - /// - /// 边界是否终止 - /// - public bool Break { get; set; } - /// - /// 边界是否终止向下探测 - /// - public bool BreakThen { get; set; } - - public AggregateRootBoundaryAttribute(string name) - { - this.Name = name; - } - public AggregateRootBoundaryAttribute() - { - } - } -} diff --git a/FreeSql.DbContext/AggregateRootRepository/AggregateRootModel.cs b/FreeSql.DbContext/AggregateRootRepository/AggregateRootModel.cs deleted file mode 100644 index 7bff61bd..00000000 --- a/FreeSql.DbContext/AggregateRootRepository/AggregateRootModel.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace FreeSql.Internal.Model -{ - - public class AggregateRootTrackingChangeInfo - { - - public List> InsertLog { get; } = new List>(); - - public List>> UpdateLog { get; } = new List>>(); - - public List> DeleteLog { get; } = new List>(); - - } -} \ No newline at end of file diff --git a/FreeSql.DbContext/AggregateRootRepository/AggregateRootRepository.cs b/FreeSql.DbContext/AggregateRootRepository/AggregateRootRepository.cs deleted file mode 100644 index ed043f80..00000000 --- a/FreeSql.DbContext/AggregateRootRepository/AggregateRootRepository.cs +++ /dev/null @@ -1,261 +0,0 @@ -using FreeSql.Extensions.EntityUtil; -using FreeSql.Internal.Model; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - -namespace FreeSql -{ - public interface IAggregateRootRepository: IBaseRepository where TEntity : class - { - IBaseRepository ChangeBoundary(string name); - } - - public partial class AggregateRootRepository : IAggregateRootRepository where TEntity : class - { - readonly IBaseRepository _repository; - public AggregateRootRepository(IFreeSql fsql) - { - if (fsql == null) throw new ArgumentNullException(nameof(fsql)); - _repository = fsql.GetRepository(); - _repository.DbContextOptions.EnableCascadeSave = false; - } - public AggregateRootRepository(IFreeSql fsql, UnitOfWorkManager uowManager) : this(uowManager?.Orm ?? fsql) - { - uowManager?.Binding(_repository); - } - public void Dispose() - { - DisposeChildRepositorys(); - _repository.FlushState(); - _repository.Dispose(); - FlushState(); - } - - string _boundaryName = ""; - public IBaseRepository ChangeBoundary(string name) - { - DisposeChildRepositorys(); - _repository.FlushState(); - FlushState(); - _boundaryName = string.Concat(name).Trim(); - return this; - } - - public IFreeSql Orm => _repository.Orm; - public IUnitOfWork UnitOfWork { get => _repository.UnitOfWork; set => _repository.UnitOfWork = value; } - public DbContextOptions DbContextOptions - { - get => _repository.DbContextOptions; - set - { - if (value == null) throw new ArgumentNullException(nameof(DbContextOptions)); - _repository.DbContextOptions = value; - _repository.DbContextOptions.EnableCascadeSave = false; - } - } - public void AsType(Type entityType) => _repository.AsType(entityType); - Func _asTableRule; - public void AsTable(Func rule) - { - _repository.AsTable(rule); - _asTableRule = rule; - } - public Type EntityType => _repository.EntityType; - public IDataFilter DataFilter => _repository.DataFilter; - - public void Attach(TEntity entity) - { - var state = CreateEntityState(entity); - if (_states.ContainsKey(state.Key)) _states[state.Key] = state; - else _states.Add(state.Key, state); - } - public void Attach(IEnumerable entity) - { - foreach (var item in entity) - Attach(item); - } - public IBaseRepository AttachOnlyPrimary(TEntity data) => _repository.AttachOnlyPrimary(data); - public Dictionary CompareState(TEntity newdata) - { - if (newdata == null) return null; - var _table = Orm.CodeFirst.GetTableByEntity(EntityType); - if (_table.Primarys.Any() == false) throw new Exception(DbContextStrings.Incomparable_EntityHasNo_PrimaryKey(Orm.GetEntityString(EntityType, newdata))); - var key = Orm.GetEntityKeyString(EntityType, newdata, false); - if (string.IsNullOrEmpty(key)) throw new Exception(DbContextStrings.Incomparable_PrimaryKey_NotSet(Orm.GetEntityString(EntityType, newdata))); - if (_states.TryGetValue(key, out var oldState) == false || oldState == null) throw new Exception($"不可对比,数据未被跟踪:{Orm.GetEntityString(EntityType, newdata)}"); - AggregateRootTrackingChangeInfo tracking = new AggregateRootTrackingChangeInfo(); - AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, oldState, newdata, null, tracking); - return new Dictionary - { - ["Insert"] = tracking.InsertLog.Select(a => new object[] { a.Item1, a.Item2 }).ToArray(), - ["Delete"] = tracking.DeleteLog.Select(a => new object[] { a.Item1, a.Item2 }).ToArray(), - ["Update"] = tracking.UpdateLog.Select(a => new object[] { a.Item1, a.Item2, a.Item3, a.Item4 }).ToArray(), - }; - } - public void FlushState() - { - DisposeChildRepositorys(); - _repository.FlushState(); - _states.Clear(); - } - - public IUpdate UpdateDiy => _repository.UpdateDiy; - public ISelect Where(Expression> exp) => Select.Where(exp); - public ISelect WhereIf(bool condition, Expression> exp) => Select.WhereIf(condition, exp); - - readonly Dictionary> _childRepositorys = new Dictionary>(); - IBaseRepository GetChildRepository(Type type) - { - if (_childRepositorys.TryGetValue(type, out var repo) == false) - { - repo = Orm.GetRepository(); - repo.AsType(type); - _childRepositorys.Add(type, repo); - } - repo.UnitOfWork = UnitOfWork; - repo.DbContextOptions = DbContextOptions; - repo.DbContextOptions.EnableCascadeSave = false; - repo.AsTable(_asTableRule); - return repo; - } - void DisposeChildRepositorys() - { - foreach (var repo in _childRepositorys.Values) - { - repo.FlushState(); - repo.Dispose(); - } - _childRepositorys.Clear(); - } - - #region 状态管理 - protected Dictionary _states = new Dictionary(); - protected class EntityState - { - public EntityState(TEntity value, string key) - { - this.Value = value; - this.Key = key; - this.Time = DateTime.Now; - } - public TEntity OldValue { get; set; } - public TEntity Value { get; set; } - public string Key { get; set; } - public DateTime Time { get; set; } - } - EntityState CreateEntityState(TEntity data) - { - if (data == null) throw new ArgumentNullException(nameof(data)); - var key = Orm.GetEntityKeyString(EntityType, data, false); - var state = new EntityState((TEntity)EntityType.CreateInstanceGetDefaultValue(), key); - AggregateRootUtils.MapEntityValue(_boundaryName, Orm, EntityType, data, state.Value); - return state; - } - bool? ExistsInStates(object data) - { - if (data == null) throw new ArgumentNullException(nameof(data)); - var key = Orm.GetEntityKeyString(EntityType, data, false); - if (string.IsNullOrEmpty(key)) return null; - return _states.ContainsKey(key); - } - #endregion - - #region 查询数据 - /// - /// 默认:创建查询对象(递归包含 Include/IncludeMany 边界之内的导航属性) - /// 重写:使用 - /// - public virtual ISelect Select => SelectAggregateRoot; - /// - /// 创建查询对象(纯净) - /// _ - /// 聚合根内关系较复杂时,获取 Include/IncludeMany 字符串代码,方便二次开发 - /// string code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(null, fsql, typeof(Order)) - /// - protected ISelect SelectDiy => _repository.Select.TrackToList(SelectAggregateRootTracking); - /// - /// 创建查询对象(递归包含 Include/IncludeMany 边界之内的导航属性) - /// - /// - protected ISelect SelectAggregateRoot - { - get - { - var query = _repository.Select.TrackToList(SelectAggregateRootTracking); - query = AggregateRootUtils.GetAutoIncludeQuery(_boundaryName, query); - return query; - } - } - /// - /// ISelect.TrackToList 委托,数据返回后自动 Attach - /// - /// - protected void SelectAggregateRootTracking(object list) - { - if (list == null) return; - var ls = list as IEnumerable; - if (ls == null) - { - var ie = list as IEnumerable; - if (ie == null) return; - var isfirst = true; - foreach (var item in ie) - { - if (item == null) continue; - if (isfirst) - { - isfirst = false; - var itemType = item.GetType(); - if (itemType == typeof(object)) return; - if (itemType.FullName.Contains("FreeSqlLazyEntity__")) itemType = itemType.BaseType; - if (Orm.CodeFirst.GetTableByEntity(itemType)?.Primarys.Any() != true) return; - if (itemType.GetConstructor(Type.EmptyTypes) == null) return; - } - if (item is TEntity item2) Attach(item2); - else return; - } - return; - } - } - //void SelectAggregateRootNavigateReader(ISelect currentQuery, Type entityType, string navigatePath, Stack ignores) - //{ - // if (ignores.Any(a => a == entityType)) return; - // ignores.Push(entityType); - // var table = Orm.CodeFirst.GetTableByEntity(entityType); - // if (table == null) return; - // if (!string.IsNullOrWhiteSpace(navigatePath)) navigatePath = $"{navigatePath}."; - // foreach (var tr in table.GetAllTableRef()) - // { - // var tbref = tr.Value; - // if (tbref.Exception != null) continue; - // var navigateExpression = $"{navigatePath}{tr.Key}"; - // switch (tbref.RefType) - // { - // case TableRefType.OneToOne: - // if (ignores.Any(a => a == tbref.RefEntityType)) break; - // currentQuery.IncludeByPropertyName(navigateExpression); - // SelectAggregateRootNavigateReader(currentQuery, tbref.RefEntityType, navigateExpression, ignores); - // break; - // case TableRefType.OneToMany: - // var ignoresCopy = new Stack(ignores.ToArray()); - // currentQuery.IncludeByPropertyName(navigateExpression, then => - // SelectAggregateRootNavigateReader(then, tbref.RefEntityType, "", ignoresCopy)); //variable 'then' of type 'FreeSql.ISelect`1[System.Object]' referenced from scope '', but it is not defined - // break; - // case TableRefType.ManyToMany: - // currentQuery.IncludeByPropertyName(navigateExpression); - // break; - // case TableRefType.PgArrayToMany: - // break; - // case TableRefType.ManyToOne: - // break; - // } - // } - // ignores.Pop(); - //} - #endregion - - } -} diff --git a/FreeSql.DbContext/AggregateRootRepository/AggregateRootRepositoryAsync.cs b/FreeSql.DbContext/AggregateRootRepository/AggregateRootRepositoryAsync.cs deleted file mode 100644 index 1da92ee8..00000000 --- a/FreeSql.DbContext/AggregateRootRepository/AggregateRootRepositoryAsync.cs +++ /dev/null @@ -1,297 +0,0 @@ -#if net40 -#else -using FreeSql.Extensions.EntityUtil; -using FreeSql.Internal; -using FreeSql.Internal.Model; -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace FreeSql -{ - partial class AggregateRootRepository - { - - #region InsertAsync - async public virtual Task InsertAsync(TEntity entity, CancellationToken cancellationToken = default) => (await InsertAsync(new[] { entity }, cancellationToken)).FirstOrDefault(); - async public virtual Task> InsertAsync(IEnumerable entitys, CancellationToken cancellationToken = default) - { - var repos = new Dictionary(); - try - { - var ret = await InsertWithinBoundaryStaticAsync(_boundaryName, _repository, GetChildRepository, entitys, null, cancellationToken); - Attach(ret); - return ret; - } - finally - { - DisposeChildRepositorys(); - _repository.FlushState(); - } - } - async Task> InsertWithinBoundaryStaticAsync(string boundaryName, IBaseRepository rootRepository, Func> getChildRepository, IEnumerable rootEntitys, int[] affrows, CancellationToken cancellationToken) where T1 : class - { - Dictionary> ignores = new Dictionary>(); - Dictionary> repos = new Dictionary>(); - var localAffrows = 0; - try - { - return await LocalInsertAsync(rootRepository, rootEntitys, true); - } - finally - { - if (affrows != null) affrows[0] = localAffrows; - } - - bool LocalCanInsert(Type entityType, object entity, bool isadd) - { - var stateKey = rootRepository.Orm.GetEntityKeyString(entityType, entity, false); - if (stateKey == null) return true; - if (ignores.TryGetValue(entityType, out var stateKeys) == false) - { - if (isadd) - { - ignores.Add(entityType, stateKeys = new Dictionary()); - stateKeys.Add(stateKey, true); - } - return true; - } - if (stateKeys.ContainsKey(stateKey) == false) - { - if (isadd) stateKeys.Add(stateKey, true); - return true; - } - return false; - } - async Task> LocalInsertAsync(IBaseRepository repository, IEnumerable entitys, bool cascade) where T2 : class - { - var table = repository.Orm.CodeFirst.GetTableByEntity(repository.EntityType); - if (table.Primarys.Any(col => col.Attribute.IsIdentity)) - { - foreach (var entity in entitys) - repository.Orm.ClearEntityPrimaryValueWithIdentity(repository.EntityType, entity); - } - var ret = await repository.InsertAsync(entitys, cancellationToken); - localAffrows += ret.Count; - foreach (var entity in entitys) LocalCanInsert(repository.EntityType, entity, true); - if (cascade == false) return ret; - - foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key)) - { - var tbref = tr.Value; - if (tbref.Exception != null) continue; - if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue; - var boundaryAttr = AggregateRootUtils.GetPropertyBoundaryAttribute(prop, boundaryName); - if (boundaryAttr?.Break == true) continue; - switch (tbref.RefType) - { - case TableRefType.OneToOne: - var otoList = ret.Select(entity => - { - var otoItem = table.GetPropertyValue(entity, prop.Name); - if (LocalCanInsert(tbref.RefEntityType, otoItem, false) == false) return null; - AggregateRootUtils.SetNavigateRelationshipValue(repository.Orm, tbref, table.Type, entity, otoItem); - return otoItem; - }).Where(entity => entity != null).ToArray(); - if (otoList.Any()) - { - var repo = getChildRepository(tbref.RefEntityType); - await LocalInsertAsync(repo, otoList, boundaryAttr?.BreakThen != true); - } - break; - case TableRefType.OneToMany: - var otmList = ret.Select(entity => - { - var otmEach = table.GetPropertyValue(entity, prop.Name) as IEnumerable; - if (otmEach == null) return null; - var otmItems = new List(); - foreach (var otmItem in otmEach) - { - if (LocalCanInsert(tbref.RefEntityType, otmItem, false) == false) continue; - otmItems.Add(otmItem); - } - AggregateRootUtils.SetNavigateRelationshipValue(repository.Orm, tbref, table.Type, entity, otmItems); - return otmItems; - }).Where(entity => entity != null).SelectMany(entity => entity).ToArray(); - if (otmList.Any()) - { - var repo = getChildRepository(tbref.RefEntityType); - await LocalInsertAsync(repo, otmList, boundaryAttr?.BreakThen != true); - } - break; - case TableRefType.ManyToMany: - var mtmMidList = new List(); - ret.ForEach(entity => - { - var mids = AggregateRootUtils.GetManyToManyObjects(repository.Orm, table, tbref, entity, prop); - if (mids != null) mtmMidList.AddRange(mids); - }); - if (mtmMidList.Any()) - { - var repo = getChildRepository(tbref.RefMiddleEntityType); - await LocalInsertAsync(repo, mtmMidList, false); - } - break; - case TableRefType.PgArrayToMany: - case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改 - break; - } - } - return ret; - } - } - #endregion - - async public virtual Task InsertOrUpdateAsync(TEntity entity, CancellationToken cancellationToken = default) - { - var stateKey = Orm.GetEntityKeyString(EntityType, entity, false); - if (entity == null) throw new ArgumentNullException(nameof(entity)); - var table = Orm.CodeFirst.GetTableByEntity(EntityType); - if (table.Primarys.Any() == false) throw new Exception(DbContextStrings.CannotAdd_EntityHasNo_PrimaryKey(Orm.GetEntityString(EntityType, entity))); - - var flagExists = ExistsInStates(entity); - if (flagExists == false) - { - var olddata = await Select.WhereDynamic(entity).FirstAsync(cancellationToken); - flagExists = olddata != null; - } - if (flagExists == true) - { - var affrows = await UpdateAsync(entity, cancellationToken); - if (affrows > 0) return entity; - } - if (table.Primarys.Where(a => a.Attribute.IsIdentity).Count() == table.Primarys.Length) - { - Orm.ClearEntityPrimaryValueWithIdentity(EntityType, entity); - return await InsertAsync(entity, cancellationToken); - } - throw new Exception(DbContextStrings.CannotAdd_PrimaryKey_NotSet(Orm.GetEntityString(EntityType, entity))); - } - - public virtual Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default) => UpdateAsync(new[] { entity }, cancellationToken); - public virtual Task UpdateAsync(IEnumerable entitys, CancellationToken cancellationToken = default) - { - var tracking = new AggregateRootTrackingChangeInfo(); - foreach (var entity in entitys) - { - 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, null, tracking); - } - foreach (var entity in entitys) - Attach(entity); - - return SaveTrackingChangeAsync(tracking, cancellationToken); - } - - - public virtual Task DeleteAsync(TEntity entity, CancellationToken cancellationToken = default) => DeleteWithinBoundaryAsync(new[] { entity }, null, cancellationToken); - public virtual Task DeleteAsync(IEnumerable entitys, CancellationToken cancellationToken = default) => DeleteWithinBoundaryAsync(entitys, null, cancellationToken); - async public virtual Task DeleteAsync(Expression> predicate, CancellationToken cancellationToken = default) => await DeleteWithinBoundaryAsync(await SelectAggregateRoot.Where(predicate).ToListAsync(cancellationToken), null, cancellationToken); - async public virtual Task> DeleteCascadeByDatabaseAsync(Expression> predicate, CancellationToken cancellationToken = default) - { - var deletedOutput = new List(); - await DeleteWithinBoundaryAsync(await SelectAggregateRoot.Where(predicate).ToListAsync(cancellationToken), deletedOutput, cancellationToken); - return deletedOutput; - } - async Task DeleteWithinBoundaryAsync(IEnumerable entitys, List deletedOutput, CancellationToken cancellationToken) - { - var tracking = new AggregateRootTrackingChangeInfo(); - foreach (var entity in entitys) - { - var stateKey = Orm.GetEntityKeyString(EntityType, entity, false); - AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, entity, null, null, tracking); - _states.Remove(stateKey); - } - var affrows = 0; - for (var a = tracking.DeleteLog.Count - 1; a >= 0; a--) - { - affrows += await Orm.Delete().AsType(tracking.DeleteLog[a].Item1).AsTable(_asTableRule) - .WhereDynamic(tracking.DeleteLog[a].Item2).ExecuteAffrowsAsync(cancellationToken); - if (deletedOutput != null) deletedOutput.AddRange(tracking.DeleteLog[a].Item2); - UnitOfWork?.EntityChangeReport?.Report.AddRange(tracking.DeleteLog[a].Item2.Select(x => - new DbContext.EntityChangeReport.ChangeInfo - { - Type = DbContext.EntityChangeType.Delete, - EntityType = tracking.DeleteLog[a].Item1, - Object = x - })); - } - 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); - Attach(entity); //应该只存储 propertyName 内容 - await SaveTrackingChangeAsync(tracking, cancellationToken); - } - - - async Task SaveTrackingChangeAsync(AggregateRootTrackingChangeInfo tracking, CancellationToken cancellationToken) - { - var affrows = 0; - DisposeChildRepositorys(); - var insertLogDict = tracking.InsertLog.GroupBy(a => a.Item1).ToDictionary(a => a.Key, a => tracking.InsertLog.Where(b => b.Item1 == a.Key).Select(b => b.Item2).ToArray()); - foreach (var il in insertLogDict) - { - var repo = GetChildRepository(il.Key); - var affrowsOut = new int[1]; - await InsertWithinBoundaryStaticAsync(_boundaryName, repo, GetChildRepository, il.Value, affrowsOut, cancellationToken); - affrows += affrowsOut[0]; - } - - for (var a = tracking.DeleteLog.Count - 1; a >= 0; a--) - { - affrows += await Orm.Delete().AsType(tracking.DeleteLog[a].Item1).AsTable(_asTableRule) - .WhereDynamic(tracking.DeleteLog[a].Item2).ExecuteAffrowsAsync(cancellationToken); - UnitOfWork?.EntityChangeReport?.Report.AddRange(tracking.DeleteLog[a].Item2.Select(x => - new DbContext.EntityChangeReport.ChangeInfo - { - Type = DbContext.EntityChangeType.Delete, - EntityType = tracking.DeleteLog[a].Item1, - Object = x - })); - } - - var updateLogDict = tracking.UpdateLog.GroupBy(a => a.Item1).ToDictionary(a => a.Key, a => tracking.UpdateLog.Where(b => b.Item1 == a.Key).Select(b => new - { - BeforeObject = b.Item2, - AfterObject = b.Item3, - UpdateColumns = b.Item4, - UpdateColumnsString = string.Join(",", b.Item4.OrderBy(c => c)) - }).ToArray()); - var updateLogDict2 = updateLogDict.ToDictionary(a => a.Key, a => a.Value.ToDictionary(b => b.UpdateColumnsString, b => a.Value.Where(c => c.UpdateColumnsString == b.UpdateColumnsString).ToArray())); - foreach (var dl in updateLogDict2) - { - foreach (var dl2 in dl.Value) - { - affrows += await Orm.Update().AsType(dl.Key).AsTable(_asTableRule) - .SetSource(dl2.Value.Select(a => a.AfterObject).ToArray()) - .UpdateColumns(dl2.Value.First().UpdateColumns.ToArray()) - .ExecuteAffrowsAsync(cancellationToken); - UnitOfWork?.EntityChangeReport?.Report.AddRange(dl2.Value.Select(x => - new DbContext.EntityChangeReport.ChangeInfo - { - Type = DbContext.EntityChangeType.Update, - EntityType = dl.Key, - Object = x.AfterObject, - BeforeObject = x.BeforeObject - })); - } - } - DisposeChildRepositorys(); - return affrows; - } - } -} -#endif \ No newline at end of file diff --git a/FreeSql.DbContext/AggregateRootRepository/AggregateRootRepositorySync.cs b/FreeSql.DbContext/AggregateRootRepository/AggregateRootRepositorySync.cs deleted file mode 100644 index cdd18e00..00000000 --- a/FreeSql.DbContext/AggregateRootRepository/AggregateRootRepositorySync.cs +++ /dev/null @@ -1,347 +0,0 @@ -using FreeSql.Extensions.EntityUtil; -using FreeSql.Internal; -using FreeSql.Internal.Model; -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace FreeSql -{ - partial class AggregateRootRepository - { - - #region BeginEdit/EndEdit - List _dataEditing; - ConcurrentDictionary _statesEditing = new ConcurrentDictionary(); - public virtual void BeginEdit(List data) - { - if (data == null) return; - var table = Orm.CodeFirst.GetTableByEntity(EntityType); - if (table.Primarys.Any() == false) throw new Exception(DbContextStrings.CannotEdit_EntityHasNo_PrimaryKey(Orm.GetEntityString(EntityType, data.First()))); - _statesEditing.Clear(); - _dataEditing = data; - foreach (var item in data) - { - var key = Orm.GetEntityKeyString(EntityType, item, false); - if (string.IsNullOrEmpty(key)) continue; - - _statesEditing.AddOrUpdate(key, k => CreateEntityState(item), (k, ov) => - { - AggregateRootUtils.MapEntityValue(_boundaryName, Orm, EntityType, item, ov.Value); - ov.Time = DateTime.Now; - return ov; - }); - } - } - public virtual int EndEdit(List data = null) - { - if (data == null) data = _dataEditing; - if (data == null) return 0; - var tracking = new AggregateRootTrackingChangeInfo(); - try - { - var addList = new List(); - var ediList = new List(); - foreach (var item in data) - { - var key = Orm.GetEntityKeyString(EntityType, item, false); - if (_statesEditing.TryRemove(key, out var state) == false) - { - tracking.InsertLog.Add(NativeTuple.Create(EntityType, (object)item)); - continue; - } - _states[key] = state; - AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, state.Value, item, null, tracking); - } - foreach (var item in _statesEditing.Values.OrderBy(a => a.Time)) - AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, item, null, null, tracking); - - return SaveTrackingChange(tracking); - } - finally - { - _dataEditing = null; - _statesEditing.Clear(); - } - } - #endregion - - #region Insert - public virtual TEntity Insert(TEntity entity) => Insert(new[] { entity }).FirstOrDefault(); - public virtual List Insert(IEnumerable entitys) - { - var repos = new Dictionary(); - try - { - var ret = InsertWithinBoundaryStatic(_boundaryName, _repository, GetChildRepository, entitys, out var affrows); - Attach(ret); - return ret; - } - finally - { - DisposeChildRepositorys(); - _repository.FlushState(); - } - } - static List InsertWithinBoundaryStatic(string boundaryName, IBaseRepository rootRepository, Func> getChildRepository, IEnumerable rootEntitys, out int affrows) where T1 : class { - Dictionary> ignores = new Dictionary>(); - Dictionary> repos = new Dictionary>(); - var localAffrows = 0; - try - { - return LocalInsert(rootRepository, rootEntitys, true); - } - finally - { - affrows = localAffrows; - } - - bool LocalCanInsert(Type entityType, object entity, bool isadd) - { - var stateKey = rootRepository.Orm.GetEntityKeyString(entityType, entity, false); - if (stateKey == null) return true; - if (ignores.TryGetValue(entityType, out var stateKeys) == false) - { - if (isadd) - { - ignores.Add(entityType, stateKeys = new Dictionary()); - stateKeys.Add(stateKey, true); - } - return true; - } - if (stateKeys.ContainsKey(stateKey) == false) - { - if (isadd) stateKeys.Add(stateKey, true); - return true; - } - return false; - } - List LocalInsert(IBaseRepository repository, IEnumerable entitys, bool cascade) where T2 : class - { - var table = repository.Orm.CodeFirst.GetTableByEntity(repository.EntityType); - if (table.Primarys.Any(col => col.Attribute.IsIdentity)) - { - foreach (var entity in entitys) - repository.Orm.ClearEntityPrimaryValueWithIdentity(repository.EntityType, entity); - } - var ret = repository.Insert(entitys); - localAffrows += ret.Count; - foreach (var entity in entitys) LocalCanInsert(repository.EntityType, entity, true); - if (cascade == false) return ret; - - foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key)) - { - var tbref = tr.Value; - if (tbref.Exception != null) continue; - if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue; - var boundaryAttr = AggregateRootUtils.GetPropertyBoundaryAttribute(prop, boundaryName); - if (boundaryAttr?.Break == true) continue; - switch (tbref.RefType) - { - case TableRefType.OneToOne: - var otoList = ret.Select(entity => - { - var otoItem = table.GetPropertyValue(entity, prop.Name); - if (LocalCanInsert(tbref.RefEntityType, otoItem, false) == false) return null; - AggregateRootUtils.SetNavigateRelationshipValue(repository.Orm, tbref, table.Type, entity, otoItem); - return otoItem; - }).Where(entity => entity != null).ToArray(); - if (otoList.Any()) - { - var repo = getChildRepository(tbref.RefEntityType); - LocalInsert(repo, otoList, boundaryAttr?.BreakThen != true); - } - break; - case TableRefType.OneToMany: - var otmList = ret.Select(entity => - { - var otmEach = table.GetPropertyValue(entity, prop.Name) as IEnumerable; - if (otmEach == null) return null; - var otmItems = new List(); - foreach (var otmItem in otmEach) - { - if (LocalCanInsert(tbref.RefEntityType, otmItem, false) == false) continue; - otmItems.Add(otmItem); - } - AggregateRootUtils.SetNavigateRelationshipValue(repository.Orm, tbref, table.Type, entity, otmItems); - return otmItems; - }).Where(entity => entity != null).SelectMany(entity => entity).ToArray(); - if (otmList.Any()) - { - var repo = getChildRepository(tbref.RefEntityType); - LocalInsert(repo, otmList, boundaryAttr?.BreakThen != true); - } - break; - case TableRefType.ManyToMany: - var mtmMidList = new List(); - ret.ForEach(entity => - { - var mids = AggregateRootUtils.GetManyToManyObjects(repository.Orm, table, tbref, entity, prop); - if (mids != null) mtmMidList.AddRange(mids); - }); - if (mtmMidList.Any()) - { - var repo = getChildRepository(tbref.RefMiddleEntityType); - LocalInsert(repo, mtmMidList, false); - } - break; - case TableRefType.PgArrayToMany: - case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改 - break; - } - } - return ret; - } - } - #endregion - - public virtual TEntity InsertOrUpdate(TEntity entity) - { - var stateKey = Orm.GetEntityKeyString(EntityType, entity, false); - if (entity == null) throw new ArgumentNullException(nameof(entity)); - var table = Orm.CodeFirst.GetTableByEntity(EntityType); - if (table.Primarys.Any() == false) throw new Exception(DbContextStrings.CannotAdd_EntityHasNo_PrimaryKey(Orm.GetEntityString(EntityType, entity))); - - var flagExists = ExistsInStates(entity); - if (flagExists == false) - { - var olddata = Select.WhereDynamic(entity).First(); - flagExists = olddata != null; - } - if (flagExists == true) - { - var affrows = Update(entity); - if (affrows > 0) return entity; - } - if (table.Primarys.Where(a => a.Attribute.IsIdentity).Count() == table.Primarys.Length) - { - Orm.ClearEntityPrimaryValueWithIdentity(EntityType, entity); - return Insert(entity); - } - throw new Exception(DbContextStrings.CannotAdd_PrimaryKey_NotSet(Orm.GetEntityString(EntityType, entity))); - } - - public virtual int Update(TEntity entity) => Update(new[] { entity }); - public virtual int Update(IEnumerable entitys) - { - var tracking = new AggregateRootTrackingChangeInfo(); - foreach(var entity in entitys) - { - 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, null, tracking); - } - foreach (var entity in entitys) - Attach(entity); - - return SaveTrackingChange(tracking); - } - - public virtual int Delete(TEntity entity) => DeleteWithinBoundary(new[] { entity }, null); - public virtual int Delete(IEnumerable entitys) => DeleteWithinBoundary(entitys, null); - public virtual int Delete(Expression> predicate) => DeleteWithinBoundary(SelectAggregateRoot.Where(predicate).ToList(), null); - public virtual List DeleteCascadeByDatabase(Expression> predicate) - { - var deletedOutput = new List(); - DeleteWithinBoundary(SelectAggregateRoot.Where(predicate).ToList(), deletedOutput); - return deletedOutput; - } - int DeleteWithinBoundary(IEnumerable entitys, List deletedOutput) - { - var tracking = new AggregateRootTrackingChangeInfo(); - foreach (var entity in entitys) - { - var stateKey = Orm.GetEntityKeyString(EntityType, entity, false); - AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, entity, null, null, tracking); - _states.Remove(stateKey); - } - var affrows = 0; - for (var a = tracking.DeleteLog.Count - 1; a >= 0; a--) - { - affrows += Orm.Delete().AsType(tracking.DeleteLog[a].Item1).AsTable(_asTableRule) - .WhereDynamic(tracking.DeleteLog[a].Item2).ExecuteAffrows(); - if (deletedOutput != null) deletedOutput.AddRange(tracking.DeleteLog[a].Item2); - UnitOfWork?.EntityChangeReport?.Report.AddRange(tracking.DeleteLog[a].Item2.Select(x => - new DbContext.EntityChangeReport.ChangeInfo - { - Type = DbContext.EntityChangeType.Delete, - EntityType = tracking.DeleteLog[a].Item1, - Object = x - })); - } - return affrows; - } - - public virtual void SaveMany(TEntity entity, string propertyName) - { - 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); - Attach(entity); //应该只存储 propertyName 内容 - SaveTrackingChange(tracking); - } - - - int SaveTrackingChange(AggregateRootTrackingChangeInfo tracking) - { - var affrows = 0; - DisposeChildRepositorys(); - var insertLogDict = tracking.InsertLog.GroupBy(a => a.Item1).ToDictionary(a => a.Key, a => tracking.InsertLog.Where(b => b.Item1 == a.Key).Select(b => b.Item2).ToArray()); - foreach (var il in insertLogDict) - { - var repo = GetChildRepository(il.Key); - InsertWithinBoundaryStatic(_boundaryName, repo, GetChildRepository, il.Value, out var affrowsOut); - affrows += affrowsOut; - } - - for (var a = tracking.DeleteLog.Count - 1; a >= 0; a--) - { - affrows += Orm.Delete().AsType(tracking.DeleteLog[a].Item1).AsTable(_asTableRule) - .WhereDynamic(tracking.DeleteLog[a].Item2).ExecuteAffrows(); - UnitOfWork?.EntityChangeReport?.Report.AddRange(tracking.DeleteLog[a].Item2.Select(x => - new DbContext.EntityChangeReport.ChangeInfo - { - Type = DbContext.EntityChangeType.Delete, - EntityType = tracking.DeleteLog[a].Item1, - Object = x - })); - } - - var updateLogDict = tracking.UpdateLog.GroupBy(a => a.Item1).ToDictionary(a => a.Key, a => tracking.UpdateLog.Where(b => b.Item1 == a.Key).Select(b => new - { - BeforeObject = b.Item2, - AfterObject = b.Item3, - UpdateColumns = b.Item4, - UpdateColumnsString = string.Join(",", b.Item4.OrderBy(c => c)) - }).ToArray()); - var updateLogDict2 = updateLogDict.ToDictionary(a => a.Key, a => a.Value.ToDictionary(b => b.UpdateColumnsString, b => a.Value.Where(c => c.UpdateColumnsString == b.UpdateColumnsString).ToArray())); - foreach (var dl in updateLogDict2) - { - foreach (var dl2 in dl.Value) - { - affrows += Orm.Update().AsType(dl.Key).AsTable(_asTableRule) - .SetSource(dl2.Value.Select(a => a.AfterObject).ToArray()) - .UpdateColumns(dl2.Value.First().UpdateColumns.ToArray()) - .ExecuteAffrows(); - UnitOfWork?.EntityChangeReport?.Report.AddRange(dl2.Value.Select(x => - new DbContext.EntityChangeReport.ChangeInfo - { - Type = DbContext.EntityChangeType.Update, - EntityType = dl.Key, - Object = x.AfterObject, - BeforeObject = x.BeforeObject - })); - } - } - DisposeChildRepositorys(); - return affrows; - } - } -} diff --git a/FreeSql.DbContext/AggregateRootRepository/AggregateRootUtils.cs b/FreeSql.DbContext/AggregateRootRepository/AggregateRootUtils.cs deleted file mode 100644 index 3f09558c..00000000 --- a/FreeSql.DbContext/AggregateRootRepository/AggregateRootUtils.cs +++ /dev/null @@ -1,652 +0,0 @@ -using FreeSql; -using FreeSql.DataAnnotations; -using FreeSql.Extensions.EntityUtil; -using FreeSql.Internal; -using FreeSql.Internal.CommonProvider; -using FreeSql.Internal.Model; -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Linq.Expressions; -using System.Reflection; -using System.Text; - -namespace FreeSql -{ - public class AggregateRootUtils - { - static ConcurrentDictionary> _dicGetPropertyBoundaryAttribute = new ConcurrentDictionary>(); - public static AggregateRootBoundaryAttribute GetPropertyBoundaryAttribute(PropertyInfo prop, string boundaryName) - { - if (boundaryName == null) return null; - return _dicGetPropertyBoundaryAttribute.GetOrAdd(prop, tp => new ConcurrentDictionary()) - .GetOrAdd(boundaryName, bn => - { - var attrs = prop.GetCustomAttributes(typeof(AggregateRootBoundaryAttribute), false); - if (attrs == null || attrs.Any() == false) return null; - return attrs.Select(a => a as AggregateRootBoundaryAttribute).Where(a => a.Name == bn).FirstOrDefault(); - }); - } - - public static void CompareEntityValue(string boundaryName, IFreeSql fsql, Type rootEntityType, object rootEntityBefore, object rootEntityAfter, string rootNavigatePropertyName, AggregateRootTrackingChangeInfo tracking) - { - Dictionary> ignores = new Dictionary>(); - LocalCompareEntityValue(rootEntityType, rootEntityBefore, rootEntityAfter, rootNavigatePropertyName, true); - ignores.Clear(); - - void LocalCompareEntityValue(Type entityType, object entityBefore, object entityAfter, string navigatePropertyName, bool cascade) - { - if (entityType == null) entityType = entityBefore?.GetType() ?? entityAfter?.GetType(); - - if (entityBefore != null) - { - var stateKey = $":before://{fsql.GetEntityKeyString(entityType, entityBefore, false)}"; - if (ignores.TryGetValue(entityType, out var stateKeys) == false) ignores.Add(entityType, stateKeys = new Dictionary()); - if (stateKeys.ContainsKey(stateKey)) return; - stateKeys.Add(stateKey, true); - } - if (entityAfter != null) - { - var stateKey = $":after://{fsql.GetEntityKeyString(entityType, entityAfter, false)}"; - if (ignores.TryGetValue(entityType, out var stateKeys) == false) ignores.Add(entityType, stateKeys = new Dictionary()); - if (stateKeys.ContainsKey(stateKey)) return; - stateKeys.Add(stateKey, true); - } - - var table = fsql.CodeFirst.GetTableByEntity(entityType); - if (table == null) return; - if (entityBefore == null && entityAfter == null) return; - if (entityBefore == null && entityAfter != null) - { - tracking.InsertLog.Add(NativeTuple.Create(entityType, entityAfter)); - return; - } - if (entityBefore != null && entityAfter == null) - { - tracking.DeleteLog.Add(NativeTuple.Create(entityType, new[] { entityBefore })); - NavigateReader(boundaryName, fsql, entityType, entityBefore, (path, tr, ct, stackvs) => - { - var dellist = stackvs.Last() as object[] ?? new[] { stackvs.Last() }; - tracking.DeleteLog.Add(NativeTuple.Create(ct, dellist)); - }); - return; - } - var changes = new List(); - foreach (var col in table.ColumnsByCs.Values) - { - if (table.ColumnsByCsIgnore.ContainsKey(col.CsName)) continue; - if (table.ColumnsByCs.ContainsKey(col.CsName)) - { - if (col.Attribute.IsVersion) continue; - var propvalBefore = table.GetPropertyValue(entityBefore, col.CsName); - var propvalAfter = table.GetPropertyValue(entityAfter, col.CsName); - //if (object.Equals(propvalBefore, propvalAfter) == false) changes.Add(col.CsName); - if (CompareEntityPropertyValue(col.CsType, propvalBefore, propvalAfter) == false) changes.Add(col.CsName); - continue; - } - } - if (changes.Any()) tracking.UpdateLog.Add(NativeTuple.Create(entityType, entityBefore, entityAfter, changes)); - if (cascade == false) return; - - foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key)) - { - var tbref = tr.Value; - if (tbref.Exception != null) continue; - if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue; - var boundaryAttr = GetPropertyBoundaryAttribute(prop, boundaryName); - if (boundaryAttr?.Break == true) continue; - if (navigatePropertyName != null && prop.Name != navigatePropertyName) continue; - var propvalBefore = table.GetPropertyValue(entityBefore, prop.Name); - var propvalAfter = table.GetPropertyValue(entityAfter, prop.Name); - switch (tbref.RefType) - { - case TableRefType.OneToOne: - SetNavigateRelationshipValue(fsql, tbref, table.Type, entityBefore, propvalBefore); - SetNavigateRelationshipValue(fsql, tbref, table.Type, entityAfter, propvalAfter); - LocalCompareEntityValue(tbref.RefEntityType, propvalBefore, propvalAfter, null, boundaryAttr?.BreakThen != true); - break; - case TableRefType.OneToMany: - SetNavigateRelationshipValue(fsql, tbref, table.Type, entityBefore, propvalBefore); - SetNavigateRelationshipValue(fsql, tbref, table.Type, entityAfter, propvalAfter); - LocalCompareEntityValueCollection(tbref.RefEntityType, propvalBefore as IEnumerable, propvalAfter as IEnumerable, boundaryAttr?.BreakThen != true); - break; - case TableRefType.ManyToMany: - var middleValuesBefore = GetManyToManyObjects(fsql, table, tbref, entityBefore, prop); - var middleValuesAfter = GetManyToManyObjects(fsql, table, tbref, entityAfter, prop); - LocalCompareEntityValueCollection(tbref.RefMiddleEntityType, middleValuesBefore as IEnumerable, middleValuesAfter as IEnumerable, false); - break; - case TableRefType.PgArrayToMany: - case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改 - break; - } - } - } - void LocalCompareEntityValueCollection(Type elementType, IEnumerable collectionBefore, IEnumerable collectionAfter, bool cascade) - { - if (collectionBefore == null && collectionAfter == null) return; - if (collectionBefore == null && collectionAfter != null) - { - foreach (var item in collectionAfter) - tracking.InsertLog.Add(NativeTuple.Create(elementType, item)); - return; - } - if (collectionBefore != null && collectionAfter == null) - { - //foreach (var item in collectionBefore as IEnumerable) - //{ - // changelog.DeleteLog.Add(NativeTuple.Create(elementType, new[] { item })); - // NavigateReader(boundaryName, fsql, elementType, item, (path, tr, ct, stackvs) => - // { - // var dellist = stackvs.Last() as object[] ?? new [] { stackvs.Last() }; - // changelog.DeleteLog.Add(NativeTuple.Create(ct, dellist)); - // }); - //} - return; - } - Dictionary dictBefore = new Dictionary(); - Dictionary dictAfter = new Dictionary(); - foreach (var item in collectionBefore as IEnumerable) - { - var key = fsql.GetEntityKeyString(elementType, item, false); - if (key != null) dictBefore.Add(key, item); - } - foreach (var item in collectionAfter as IEnumerable) - { - var key = fsql.GetEntityKeyString(elementType, item, false); - if (key != null) - { - if (dictAfter.ContainsKey(key) == false) - dictAfter.Add(key, item); - } - else tracking.InsertLog.Add(NativeTuple.Create(elementType, item)); - } - foreach (var key in dictBefore.Keys.ToArray()) - { - if (dictAfter.ContainsKey(key) == false) - { - var value = dictBefore[key]; - tracking.DeleteLog.Add(NativeTuple.Create(elementType, new[] { value })); - NavigateReader(boundaryName, fsql, elementType, value, (path, tr, ct, stackvs) => - { - var dellist = stackvs.Last() as object[] ?? new[] { stackvs.Last() }; - tracking.DeleteLog.Add(NativeTuple.Create(ct, dellist)); - }); - dictBefore.Remove(key); - } - } - foreach (var key in dictAfter.Keys.ToArray()) - { - if (dictBefore.ContainsKey(key) == false) - { - tracking.InsertLog.Add(NativeTuple.Create(elementType, dictAfter[key])); - dictAfter.Remove(key); - } - } - foreach (var key in dictBefore.Keys) - LocalCompareEntityValue(elementType, dictBefore[key], dictAfter[key], null, cascade); - } - } - - static ConcurrentDictionary _dicCompareEntityPropertyValue = new ConcurrentDictionary - { - [typeof(string)] = true, - [typeof(DateTime)] = true, - [typeof(DateTime?)] = true, - [typeof(DateTimeOffset)] = true, - [typeof(DateTimeOffset?)] = true, - [typeof(TimeSpan)] = true, - [typeof(TimeSpan?)] = true, - }; - public static bool CompareEntityPropertyValue(Type type, object propvalBefore, object propvalAfter) - { - if (propvalBefore == null && propvalAfter == null) return true; - if (type.IsNumberType() || - _dicCompareEntityPropertyValue.ContainsKey(type) || - type.IsEnum || - type.IsValueType || - type.NullableTypeOrThis().IsEnum) return object.Equals(propvalBefore, propvalAfter); - if (propvalBefore == null && propvalAfter != null) return false; - if (propvalBefore != null && propvalAfter == null) return false; - - if (FreeSql.Internal.Utils.dicExecuteArrayRowReadClassOrTuple.ContainsKey(type)) { - if (type.FullName.StartsWith("Newtonsoft.")) - return object.Equals(propvalBefore.ToString(), propvalAfter.ToString()); - - if (typeof(IDictionary).IsAssignableFrom(type)) - { - var dictBefore = (propvalBefore as IDictionary); - var dictAfter = (propvalAfter as IDictionary); - if (dictBefore.Count != dictAfter.Count) return false; - foreach (var key in dictBefore.Keys) - { - if (dictAfter.Contains(key) == false) return false; - var valBefore = dictBefore[key]; - var valAfter = dictAfter[key]; - if (valBefore == null && valAfter == null) continue; - if (valBefore == null && valAfter != null) return false; - if (valBefore != null && valAfter == null) return false; - if (CompareEntityPropertyValue(valBefore.GetType(), valBefore, valAfter) == false) return false; - } - return true; - } - - if (type.IsArrayOrList()) - { - var enumableBefore = propvalBefore as IEnumerable; - var enumableAfter = propvalAfter as IEnumerable; - var itorBefore = enumableBefore.GetEnumerator(); - var itorAfter = enumableAfter.GetEnumerator(); - while (true) - { - var moveNextBefore = itorBefore.MoveNext(); - var moveNextAfter = itorAfter.MoveNext(); - if (moveNextBefore != moveNextAfter) return false; - if (moveNextBefore == false) break; - var currentBefore = itorBefore.Current; - var currentAfter = itorAfter.Current; - if (currentBefore == null && enumableAfter == null) continue; - if (currentBefore == null && currentAfter != null) return false; - if (currentBefore != null && currentAfter == null) return false; - if (CompareEntityPropertyValue(currentBefore.GetType(), currentBefore, currentAfter) == false) return false; - } - return true; - } - - if (type.FullName.StartsWith("System.") || - type.FullName.StartsWith("Npgsql.") || - type.FullName.StartsWith("NetTopologySuite.")) - return object.Equals(propvalBefore, propvalAfter); - - if (type.IsClass) - { - foreach (var prop in type.GetProperties()) - { - var valBefore = prop.GetValue(propvalBefore, new object[0]); - var valAfter = prop.GetValue(propvalAfter, new object[0]); - if (CompareEntityPropertyValue(prop.PropertyType, valBefore, valAfter) == false) return false; - } - return true; - } - } - return object.Equals(propvalBefore, propvalAfter); - } - - public static void NavigateReader(string boundaryName, IFreeSql fsql, Type rootType, object rootEntity, Action> callback) - { - Dictionary> ignores = new Dictionary>(); - var statckPath = new Stack(); - var stackValues = new List(); - statckPath.Push("_"); - stackValues.Add(rootEntity); - LocalNavigateReader(rootType, rootEntity); - ignores.Clear(); - - void LocalNavigateReader(Type entityType, object entity) - { - if (entity == null) return; - if (entityType == null) entityType = entity.GetType(); - var table = fsql.CodeFirst.GetTableByEntity(entityType); - if (table == null) return; - - var stateKey = fsql.GetEntityKeyString(entityType, entity, false); - if (ignores.TryGetValue(entityType, out var stateKeys) == false) ignores.Add(entityType, stateKeys = new Dictionary()); - if (stateKeys.ContainsKey(stateKey)) return; - stateKeys.Add(stateKey, true); - - foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key)) - { - var tbref = tr.Value; - if (tbref.Exception != null) continue; - if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue; - var boundaryAttr = GetPropertyBoundaryAttribute(prop, boundaryName); - if (boundaryAttr?.Break == true) continue; - switch (tbref.RefType) - { - case TableRefType.OneToOne: - var propval = table.GetPropertyValue(entity, prop.Name); - if (propval == null) continue; - statckPath.Push(prop.Name); - stackValues.Add(propval); - SetNavigateRelationshipValue(fsql, tbref, table.Type, entity, propval); - callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefEntityType, stackValues); - if (boundaryAttr?.BreakThen != true) - LocalNavigateReader(tbref.RefEntityType, propval); - stackValues.RemoveAt(stackValues.Count - 1); - statckPath.Pop(); - break; - case TableRefType.OneToMany: - var propvalOtm = table.GetPropertyValue(entity, prop.Name) as IEnumerable; - if (propvalOtm == null) continue; - SetNavigateRelationshipValue(fsql, tbref, table.Type, entity, propvalOtm); - var propvalOtmList = new List(); - foreach (var val in propvalOtm) - propvalOtmList.Add(val); - statckPath.Push($"{prop.Name}[]"); - stackValues.Add(propvalOtmList.ToArray()); - callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefEntityType, stackValues); - if (boundaryAttr?.BreakThen != true) - foreach (var val in propvalOtm) - LocalNavigateReader(tbref.RefEntityType, val); - stackValues.RemoveAt(stackValues.Count - 1); - statckPath.Pop(); - break; - case TableRefType.ManyToMany: - var middleValues = GetManyToManyObjects(fsql, table, tbref, entity, prop).ToArray(); - if (middleValues == null) continue; - statckPath.Push($"{prop.Name}[]"); - stackValues.Add(middleValues); - callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefMiddleEntityType, stackValues); - stackValues.RemoveAt(stackValues.Count - 1); - statckPath.Pop(); - break; - case TableRefType.PgArrayToMany: - case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改 - break; - } - } - } - } - - public static void MapEntityValue(string boundaryName, IFreeSql fsql, Type rootEntityType, object rootEntityFrom, object rootEntityTo) - { - Dictionary> ignores = new Dictionary>(); - LocalMapEntityValue(rootEntityType, rootEntityFrom, rootEntityTo, true); - ignores.Clear(); - - void LocalMapEntityValue(Type entityType, object entityFrom, object entityTo, bool cascade) - { - if (entityFrom == null || entityTo == null) return; - if (entityType == null) entityType = entityFrom?.GetType() ?? entityTo?.GetType(); - var table = fsql.CodeFirst.GetTableByEntity(entityType); - if (table == null) return; - - var stateKey = fsql.GetEntityKeyString(entityType, entityFrom, false); - if (ignores.TryGetValue(entityType, out var stateKeys) == false) ignores.Add(entityType, stateKeys = new Dictionary()); - if (stateKeys.ContainsKey(stateKey)) return; - stateKeys.Add(stateKey, true); - - foreach (var prop in table.Properties.Values) - { - if (table.ColumnsByCsIgnore.ContainsKey(prop.Name)) continue; - if (table.ColumnsByCs.ContainsKey(prop.Name)) - { - table.SetPropertyValue(entityTo, prop.Name, table.GetPropertyValue(entityFrom, prop.Name)); - continue; - } - if (cascade == false) continue; - var tbref = table.GetTableRef(prop.Name, false); - if (tbref == null) continue; - var boundaryAttr = GetPropertyBoundaryAttribute(prop, boundaryName); - if (boundaryAttr?.Break == true) continue; - var propvalFrom = EntityUtilExtensions.GetEntityValueWithPropertyName(fsql, entityType, entityFrom, prop.Name); - if (propvalFrom == null) - { - EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, null); - return; - } - switch (tbref.RefType) - { - case TableRefType.OneToOne: - var propvalTo = tbref.RefEntityType.CreateInstanceGetDefaultValue(); - SetNavigateRelationshipValue(fsql, tbref, table.Type, entityFrom, propvalFrom); - LocalMapEntityValue(tbref.RefEntityType, propvalFrom, propvalTo, boundaryAttr?.BreakThen != true); - EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, propvalTo); - break; - case TableRefType.OneToMany: - SetNavigateRelationshipValue(fsql, tbref, table.Type, entityFrom, propvalFrom); - LocalMapEntityValueCollection(entityType, entityFrom, entityTo, tbref, propvalFrom as IEnumerable, prop, boundaryAttr?.BreakThen != true); - break; - case TableRefType.ManyToMany: - LocalMapEntityValueCollection(entityType, entityFrom, entityTo, tbref, propvalFrom as IEnumerable, prop, false); - break; - case TableRefType.PgArrayToMany: - case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改 - break; - } - } - } - void LocalMapEntityValueCollection(Type entityType, object entityFrom, object entityTo, TableRef tbref, IEnumerable propvalFrom, PropertyInfo prop, bool cascade) - { - var propvalTo = typeof(List<>).MakeGenericType(tbref.RefEntityType).CreateInstanceGetDefaultValue(); - var propvalToIList = propvalTo as IList; - foreach (var fromItem in propvalFrom) - { - var toItem = tbref.RefEntityType.CreateInstanceGetDefaultValue(); - LocalMapEntityValue(tbref.RefEntityType, fromItem, toItem, cascade); - propvalToIList.Add(toItem); - } - var propvalType = prop.PropertyType.GetGenericTypeDefinition(); - if (propvalType == typeof(List<>) || propvalType == typeof(ICollection<>)) - EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, propvalTo); - else if (propvalType == typeof(ObservableCollection<>)) - { - //var propvalTypeOcCtor = typeof(ObservableCollection<>).MakeGenericType(tbref.RefEntityType).GetConstructor(new[] { typeof(List<>).MakeGenericType(tbref.RefEntityType) }); - var propvalTypeOc = Activator.CreateInstance(typeof(ObservableCollection<>).MakeGenericType(tbref.RefEntityType), new object[] { propvalTo }); - EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, propvalTypeOc); - } - } - } - - static ConcurrentDictionary>>> _dicGetAutoIncludeQuery = new ConcurrentDictionary>>>(); - public static ISelect GetAutoIncludeQuery(string boundaryName, ISelect select) - { - var select0p = select as Select0Provider; - var table0Type = select0p._tables[0].Table.Type; - var func = _dicGetAutoIncludeQuery.GetOrAdd(boundaryName ?? "", bn => new ConcurrentDictionary>>()) - .GetOrAdd(typeof(TEntity), t => new ConcurrentDictionary>()) - .GetOrAdd(table0Type, t => - { - var parmExp1 = Expression.Parameter(typeof(ISelect0)); - var parmNavigateParameterExp = Expression.Parameter(typeof(TEntity), "a"); - var parmQueryExp = Expression.Convert(parmExp1, typeof(ISelect<>).MakeGenericType(typeof(TEntity))); - var exp = LocalGetAutoIncludeQuery(parmQueryExp, 1, t, parmNavigateParameterExp, parmNavigateParameterExp, new Stack()); - return Expression.Lambda>(exp, parmExp1).Compile(); - }); - func(select); - return select; - Expression LocalGetAutoIncludeQuery(Expression queryExp, int depth, Type entityType, ParameterExpression navigateParameterExp, Expression navigatePathExp, Stack ignores) - { - if (ignores.Any(a => a == entityType)) return queryExp; - ignores.Push(entityType); - var table = select0p._orm.CodeFirst.GetTableByEntity(entityType); - if (table == null) return queryExp; - foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key)) - { - var tbref = tr.Value; - if (tbref.Exception != null) continue; - if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue; - var boundaryAttr = GetPropertyBoundaryAttribute(prop, boundaryName); - if (boundaryAttr?.Break == true) continue; - Expression navigateExp = Expression.MakeMemberAccess(navigatePathExp, prop); - //var lambdaAlias = (char)((byte)'a' + (depth - 1)); - switch (tbref.RefType) - { - case TableRefType.OneToOne: - if (ignores.Any(a => a == tbref.RefEntityType)) break; - LocalInclude(tbref, navigateExp); - if (boundaryAttr?.BreakThen != true) - queryExp = LocalGetAutoIncludeQuery(queryExp, depth, tbref.RefEntityType, navigateParameterExp, navigateExp, ignores); - break; - case TableRefType.OneToMany: - LocalIncludeMany(tbref, navigateExp, boundaryAttr?.BreakThen != true); - break; - case TableRefType.ManyToMany: - LocalIncludeMany(tbref, navigateExp, boundaryAttr?.BreakThen == false); - break; - case TableRefType.PgArrayToMany: - if (boundaryAttr?.Break == false) - LocalIncludeMany(tbref, navigateExp, boundaryAttr?.BreakThen == false); - break; - case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改 - if (boundaryAttr?.Break == false) - { - LocalInclude(tbref, navigateExp); - if (boundaryAttr?.BreakThen == false) - queryExp = LocalGetAutoIncludeQuery(queryExp, depth, tbref.RefEntityType, navigateParameterExp, navigateExp, ignores); - } - break; - } - } - ignores.Pop(); - return queryExp; - void LocalInclude(TableRef tbref, Expression exp) - { - var incMethod = queryExp.Type.GetMethod("Include"); - if (incMethod == null) throw new Exception(CoreStrings.RunTimeError_Reflection_IncludeMany.Replace("IncludeMany", "Include")); - queryExp = Expression.Call(queryExp, incMethod.MakeGenericMethod(tbref.RefEntityType), - Expression.Lambda(typeof(Func<,>).MakeGenericType(entityType, tbref.RefEntityType), exp, navigateParameterExp)); - } - void LocalIncludeMany(TableRef tbref, Expression exp, bool isthen) - { - var funcType = typeof(Func<,>).MakeGenericType(entityType, typeof(IEnumerable<>).MakeGenericType(tbref.RefEntityType)); - var navigateSelector = Expression.Lambda(funcType, exp, navigateParameterExp); - var incMethod = queryExp.Type.GetMethod("IncludeMany"); - if (incMethod == null) throw new Exception(CoreStrings.RunTimeError_Reflection_IncludeMany); - LambdaExpression navigateThen = null; - var navigateThenType = typeof(Action<>).MakeGenericType(typeof(ISelect<>).MakeGenericType(tbref.RefEntityType)); - var thenParameter = Expression.Parameter(typeof(ISelect<>).MakeGenericType(tbref.RefEntityType), "then"); - Expression paramQueryExp = thenParameter; - var paramNavigateParameterExp = Expression.Parameter(tbref.RefEntityType, string.Concat((char)((byte)'a' + (depth - 1)))); - if (isthen) paramQueryExp = LocalGetAutoIncludeQuery(paramQueryExp, depth + 1, tbref.RefEntityType, paramNavigateParameterExp, paramNavigateParameterExp, ignores); - navigateThen = Expression.Lambda(navigateThenType, paramQueryExp, thenParameter); - queryExp = Expression.Call(queryExp, incMethod.MakeGenericMethod(tbref.RefEntityType), navigateSelector, navigateThen); - } - } - } - - public static string GetAutoIncludeQueryStaicCode(string boundaryName, IFreeSql fsql, Type rootEntityType) - { - return $"//fsql.Select<{rootEntityType.Name}>()\r\nSelectDiy{LocalGetAutoIncludeQueryStaicCode(1, rootEntityType, "", new Stack())}"; - string LocalGetAutoIncludeQueryStaicCode(int depth, Type entityType, string navigatePath, Stack ignores) - { - var code = new StringBuilder(); - if (ignores.Any(a => a == entityType)) return null; - ignores.Push(entityType); - var table = fsql.CodeFirst.GetTableByEntity(entityType); - if (table == null) return null; - if (!string.IsNullOrWhiteSpace(navigatePath)) navigatePath = $"{navigatePath}."; - foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key)) - { - var tbref = tr.Value; - if (tbref.Exception != null) continue; - if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue; - var boundaryAttr = GetPropertyBoundaryAttribute(prop, boundaryName); - if (boundaryAttr?.Break == true) continue; - var navigateExpression = $"{navigatePath}{tr.Key}"; - var depthTab = "".PadLeft(depth * 4); - var lambdaAlias = (char)((byte)'a' + (depth - 1)); - var lambdaStr = $"{lambdaAlias} => {lambdaAlias}."; - switch (tbref.RefType) - { - case TableRefType.OneToOne: - if (ignores.Any(a => a == tbref.RefEntityType)) break; - code.Append("\r\n").Append(depthTab).Append(".Include(").Append(lambdaStr).Append(navigateExpression).Append(")"); - if (boundaryAttr?.BreakThen != true) - code.Append(LocalGetAutoIncludeQueryStaicCode(depth, tbref.RefEntityType, navigateExpression, ignores)); - break; - case TableRefType.OneToMany: - code.Append("\r\n").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression); - if (boundaryAttr?.BreakThen != true) - { - var thencode = LocalGetAutoIncludeQueryStaicCode(depth + 1, tbref.RefEntityType, "", new Stack(ignores.ToArray())); - if (thencode.Length > 0) code.Append(", then => then").Append(thencode); - } - code.Append(")"); - break; - case TableRefType.ManyToMany: - code.Append("\r\n").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression); - if (boundaryAttr?.BreakThen == false) - { - var thencode = LocalGetAutoIncludeQueryStaicCode(depth + 1, tbref.RefEntityType, "", new Stack(ignores.ToArray())); - if (thencode.Length > 0) code.Append(", then => then").Append(thencode); - } - code.Append(")"); - break; - case TableRefType.PgArrayToMany: - code.Append("\r\n").Append(boundaryAttr != null ? "" : "//").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression); - if (boundaryAttr?.BreakThen == false) - { - var thencode = LocalGetAutoIncludeQueryStaicCode(depth + 1, tbref.RefEntityType, "", new Stack(ignores.ToArray())); - if (thencode.Length > 0) code.Append(", then => then").Append(thencode); - } - code.Append(")"); - break; - case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改 - code.Append("\r\n").Append(boundaryAttr != null ? "" : "//").Append(depthTab).Append(".Include(").Append(lambdaStr).Append(navigateExpression).Append(")"); - if (boundaryAttr?.BreakThen == false) - code.Append(LocalGetAutoIncludeQueryStaicCode(depth, tbref.RefEntityType, navigateExpression, ignores)); - break; - } - } - ignores.Pop(); - return code.ToString(); - } - } - - public static List GetManyToManyObjects(IFreeSql fsql, TableInfo table, TableRef tbref, object entity, PropertyInfo prop) - { - if (tbref.RefType != TableRefType.ManyToMany) return null; - var rights = table.GetPropertyValue(entity, prop.Name) as IEnumerable; - if (rights == null) return null; - var middles = new List(); - var leftpkvals = new object[tbref.Columns.Count]; - for (var x = 0; x < tbref.Columns.Count; x++) - leftpkvals[x] = Utils.GetDataReaderValue(tbref.MiddleColumns[x].CsType, EntityUtilExtensions.GetEntityValueWithPropertyName(fsql, table.Type, entity, tbref.Columns[x].CsName)); - foreach (var right in rights) - { - var midval = tbref.RefMiddleEntityType.CreateInstanceGetDefaultValue(); - for (var x = 0; x < tbref.Columns.Count; x++) - EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, tbref.RefMiddleEntityType, midval, tbref.MiddleColumns[x].CsName, leftpkvals[x]); - - for (var x = tbref.Columns.Count; x < tbref.MiddleColumns.Count; x++) - { - var refcol = tbref.RefColumns[x - tbref.Columns.Count]; - var refval = EntityUtilExtensions.GetEntityValueWithPropertyName(fsql, tbref.RefEntityType, right, refcol.CsName); - if (refval == refcol.CsType.CreateInstanceGetDefaultValue()) throw new Exception($"ManyToMany 关联对象的主键属性({tbref.RefEntityType.DisplayCsharp()}.{refcol.CsName})不能为空"); - refval = Utils.GetDataReaderValue(tbref.MiddleColumns[x].CsType, refval); - EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, tbref.RefMiddleEntityType, midval, tbref.MiddleColumns[x].CsName, refval); - } - middles.Add(midval); - } - return middles; - } - - public static void SetNavigateRelationshipValue(IFreeSql orm, TableRef tbref, Type leftType, object leftItem, object rightItem) - { - switch (tbref.RefType) - { - case TableRefType.OneToOne: - if (rightItem == null) return; - for (var idx = 0; idx < tbref.Columns.Count; idx++) - { - var colval = Utils.GetDataReaderValue(tbref.RefColumns[idx].CsType, EntityUtilExtensions.GetEntityValueWithPropertyName(orm, leftType, leftItem, tbref.Columns[idx].CsName)); - EntityUtilExtensions.SetEntityValueWithPropertyName(orm, tbref.RefEntityType, rightItem, tbref.RefColumns[idx].CsName, colval); - } - break; - case TableRefType.OneToMany: - if (rightItem == null) return; - var rightEachOtm = rightItem as IEnumerable; - if (rightEachOtm == null) break; - var leftColValsOtm = new object[tbref.Columns.Count]; - for (var idx = 0; idx < tbref.Columns.Count; idx++) - leftColValsOtm[idx] = Utils.GetDataReaderValue(tbref.RefColumns[idx].CsType, EntityUtilExtensions.GetEntityValueWithPropertyName(orm, leftType, leftItem, tbref.Columns[idx].CsName)); - foreach (var rightEle in rightEachOtm) - for (var idx = 0; idx < tbref.Columns.Count; idx++) - EntityUtilExtensions.SetEntityValueWithPropertyName(orm, tbref.RefEntityType, rightEle, tbref.RefColumns[idx].CsName, leftColValsOtm[idx]); - break; - case TableRefType.ManyToOne: - for (var idx = 0; idx < tbref.RefColumns.Count; idx++) - { - var colval = rightItem == null ? - tbref.Columns[idx].CsType.CreateInstanceGetDefaultValue() : - Utils.GetDataReaderValue(tbref.Columns[idx].CsType, EntityUtilExtensions.GetEntityValueWithPropertyName(orm, tbref.RefEntityType, rightItem, tbref.RefColumns[idx].CsName)); - EntityUtilExtensions.SetEntityValueWithPropertyName(orm, leftType, leftItem, tbref.Columns[idx].CsName, colval); - } - break; - } - } - } -} \ No newline at end of file diff --git a/FreeSql.DbContext/AggregateRootRepository/FreeSqlRepositoryExtensions.cs b/FreeSql.DbContext/AggregateRootRepository/FreeSqlRepositoryExtensions.cs deleted file mode 100644 index 0f5da722..00000000 --- a/FreeSql.DbContext/AggregateRootRepository/FreeSqlRepositoryExtensions.cs +++ /dev/null @@ -1,12 +0,0 @@ -using FreeSql; -using System; -using System.Linq; -using System.Linq.Expressions; - -public static class FreeSqlAggregateRootRepositoryGlobalExtensions -{ - public static IBaseRepository GetAggregateRootRepository(this IFreeSql that) where TEntity : class - { - return new AggregateRootRepository(that); - } -} \ No newline at end of file diff --git a/FreeSql.DbContext/FreeSql - Backup.DbContext.csproj b/FreeSql.DbContext/FreeSql - Backup.DbContext.csproj index 1e45ee9e..8b4ab324 100644 --- a/FreeSql.DbContext/FreeSql - Backup.DbContext.csproj +++ b/FreeSql.DbContext/FreeSql - Backup.DbContext.csproj @@ -17,7 +17,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/FreeSql.DbContext/FreeSql.DbContext.csproj b/FreeSql.DbContext/FreeSql.DbContext.csproj index 87049638..8b4ab324 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.csproj +++ b/FreeSql.DbContext/FreeSql.DbContext.csproj @@ -17,7 +17,7 @@ true key.snk false - 3.2.666 + 3.2.668 diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 1fe1062e..26522f10 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -4,50 +4,6 @@ FreeSql.DbContext - - - 设置 AggregateRootRepository 边界范围 - 在边界范围之内的规则 : - 1、OneToOne/OneToMany/ManyToMany(中间表) 可以查询、可以增删改 - 2、ManyToOne/ManyToMany外部表/PgArrayToMany 只可以查询,不支持增删改(会被忽略) - - - - - 边界是否终止 - - - - - 边界是否终止向下探测 - - - - - 默认:创建查询对象(递归包含 Include/IncludeMany 边界之内的导航属性) - 重写:使用 - - - - - 创建查询对象(纯净) - _ - 聚合根内关系较复杂时,获取 Include/IncludeMany 字符串代码,方便二次开发 - string code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(null, fsql, typeof(Order)) - - - - - 创建查询对象(递归包含 Include/IncludeMany 边界之内的导航属性) - - - - - - ISelect.TrackToList 委托,数据返回后自动 Attach - - - 该对象 Select/Delete/Insert/Update/InsertOrUpdate 与 DbContext 事务保持一致,可省略传递 WithTransaction diff --git a/FreeSql.Repository/FreeSql.Repository.csproj b/FreeSql.Repository/FreeSql.Repository.csproj index 0eed492f..5903e1a9 100644 --- a/FreeSql.Repository/FreeSql.Repository.csproj +++ b/FreeSql.Repository/FreeSql.Repository.csproj @@ -17,7 +17,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/FreeSql.Tests/FreeSql.Tests.DbContext2/FreeSql.Tests.DbContext2.csproj b/FreeSql.Tests/FreeSql.Tests.DbContext2/FreeSql.Tests.DbContext2.csproj index 8b4feaa1..0c4164e0 100644 --- a/FreeSql.Tests/FreeSql.Tests.DbContext2/FreeSql.Tests.DbContext2.csproj +++ b/FreeSql.Tests/FreeSql.Tests.DbContext2/FreeSql.Tests.DbContext2.csproj @@ -20,6 +20,7 @@ + diff --git a/FreeSql/FreeSql.csproj b/FreeSql/FreeSql.csproj index eb8a9a2f..ceca324e 100644 --- a/FreeSql/FreeSql.csproj +++ b/FreeSql/FreeSql.csproj @@ -17,7 +17,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.ClickHouse/FreeSql.Provider.ClickHouse.csproj b/Providers/FreeSql.Provider.ClickHouse/FreeSql.Provider.ClickHouse.csproj index e8f74b42..21d940ae 100644 --- a/Providers/FreeSql.Provider.ClickHouse/FreeSql.Provider.ClickHouse.csproj +++ b/Providers/FreeSql.Provider.ClickHouse/FreeSql.Provider.ClickHouse.csproj @@ -19,7 +19,7 @@ False key.snk false - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.Custom/FreeSql.Provider.Custom.csproj b/Providers/FreeSql.Provider.Custom/FreeSql.Provider.Custom.csproj index 74751917..bb6223ed 100644 --- a/Providers/FreeSql.Provider.Custom/FreeSql.Provider.Custom.csproj +++ b/Providers/FreeSql.Provider.Custom/FreeSql.Provider.Custom.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.Dameng/FreeSql.Provider.Dameng.csproj b/Providers/FreeSql.Provider.Dameng/FreeSql.Provider.Dameng.csproj index b9d7c21f..eb6d6c5b 100644 --- a/Providers/FreeSql.Provider.Dameng/FreeSql.Provider.Dameng.csproj +++ b/Providers/FreeSql.Provider.Dameng/FreeSql.Provider.Dameng.csproj @@ -15,7 +15,7 @@ $(AssemblyName) true true - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.Firebird/FreeSql.Provider.Firebird.csproj b/Providers/FreeSql.Provider.Firebird/FreeSql.Provider.Firebird.csproj index 4a6aec2e..434418d8 100644 --- a/Providers/FreeSql.Provider.Firebird/FreeSql.Provider.Firebird.csproj +++ b/Providers/FreeSql.Provider.Firebird/FreeSql.Provider.Firebird.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.GBase/FreeSql.Provider.GBase.csproj b/Providers/FreeSql.Provider.GBase/FreeSql.Provider.GBase.csproj index 94493707..33e9b330 100644 --- a/Providers/FreeSql.Provider.GBase/FreeSql.Provider.GBase.csproj +++ b/Providers/FreeSql.Provider.GBase/FreeSql.Provider.GBase.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.KingbaseES/FreeSql.Provider.KingbaseES.csproj b/Providers/FreeSql.Provider.KingbaseES/FreeSql.Provider.KingbaseES.csproj index ac168afe..00cd2f0f 100644 --- a/Providers/FreeSql.Provider.KingbaseES/FreeSql.Provider.KingbaseES.csproj +++ b/Providers/FreeSql.Provider.KingbaseES/FreeSql.Provider.KingbaseES.csproj @@ -15,7 +15,7 @@ $(AssemblyName) true true - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.MsAccess/FreeSql.Provider.MsAccess.csproj b/Providers/FreeSql.Provider.MsAccess/FreeSql.Provider.MsAccess.csproj index 2d7d2429..c9cd30e2 100644 --- a/Providers/FreeSql.Provider.MsAccess/FreeSql.Provider.MsAccess.csproj +++ b/Providers/FreeSql.Provider.MsAccess/FreeSql.Provider.MsAccess.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.MySql/FreeSql.Provider.MySql.csproj b/Providers/FreeSql.Provider.MySql/FreeSql.Provider.MySql.csproj index cd60e7cc..439f339a 100644 --- a/Providers/FreeSql.Provider.MySql/FreeSql.Provider.MySql.csproj +++ b/Providers/FreeSql.Provider.MySql/FreeSql.Provider.MySql.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.MySqlConnector/FreeSql.Provider.MySqlConnector.csproj b/Providers/FreeSql.Provider.MySqlConnector/FreeSql.Provider.MySqlConnector.csproj index 0420eec3..b54c79bb 100644 --- a/Providers/FreeSql.Provider.MySqlConnector/FreeSql.Provider.MySqlConnector.csproj +++ b/Providers/FreeSql.Provider.MySqlConnector/FreeSql.Provider.MySqlConnector.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.Odbc/FreeSql.Provider.Odbc.csproj b/Providers/FreeSql.Provider.Odbc/FreeSql.Provider.Odbc.csproj index c5e093ba..dc09a474 100644 --- a/Providers/FreeSql.Provider.Odbc/FreeSql.Provider.Odbc.csproj +++ b/Providers/FreeSql.Provider.Odbc/FreeSql.Provider.Odbc.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.Oracle/FreeSql.Provider.Oracle.csproj b/Providers/FreeSql.Provider.Oracle/FreeSql.Provider.Oracle.csproj index d34a4373..8fd75bf5 100644 --- a/Providers/FreeSql.Provider.Oracle/FreeSql.Provider.Oracle.csproj +++ b/Providers/FreeSql.Provider.Oracle/FreeSql.Provider.Oracle.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.OracleOledb/FreeSql.Provider.OracleOledb.csproj b/Providers/FreeSql.Provider.OracleOledb/FreeSql.Provider.OracleOledb.csproj index 9429f16f..8352d8fb 100644 --- a/Providers/FreeSql.Provider.OracleOledb/FreeSql.Provider.OracleOledb.csproj +++ b/Providers/FreeSql.Provider.OracleOledb/FreeSql.Provider.OracleOledb.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.PostgreSQL/FreeSql.Provider.PostgreSQL.csproj b/Providers/FreeSql.Provider.PostgreSQL/FreeSql.Provider.PostgreSQL.csproj index 875c4dd5..b46dd194 100644 --- a/Providers/FreeSql.Provider.PostgreSQL/FreeSql.Provider.PostgreSQL.csproj +++ b/Providers/FreeSql.Provider.PostgreSQL/FreeSql.Provider.PostgreSQL.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.ShenTong/FreeSql.Provider.ShenTong.csproj b/Providers/FreeSql.Provider.ShenTong/FreeSql.Provider.ShenTong.csproj index 1e9cfdef..24808326 100644 --- a/Providers/FreeSql.Provider.ShenTong/FreeSql.Provider.ShenTong.csproj +++ b/Providers/FreeSql.Provider.ShenTong/FreeSql.Provider.ShenTong.csproj @@ -15,7 +15,7 @@ $(AssemblyName) true true - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.SqlServer/FreeSql.Provider.SqlServer.csproj b/Providers/FreeSql.Provider.SqlServer/FreeSql.Provider.SqlServer.csproj index 91182fd9..5bf53058 100644 --- a/Providers/FreeSql.Provider.SqlServer/FreeSql.Provider.SqlServer.csproj +++ b/Providers/FreeSql.Provider.SqlServer/FreeSql.Provider.SqlServer.csproj @@ -18,7 +18,7 @@ true false key.snk - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.SqlServerForSystem/FreeSql.Provider.SqlServerForSystem.csproj b/Providers/FreeSql.Provider.SqlServerForSystem/FreeSql.Provider.SqlServerForSystem.csproj index fbabfb5e..e4df8f00 100644 --- a/Providers/FreeSql.Provider.SqlServerForSystem/FreeSql.Provider.SqlServerForSystem.csproj +++ b/Providers/FreeSql.Provider.SqlServerForSystem/FreeSql.Provider.SqlServerForSystem.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.Sqlite/FreeSql.Provider.Sqlite.csproj b/Providers/FreeSql.Provider.Sqlite/FreeSql.Provider.Sqlite.csproj index 6f1767cf..6cb9fe5e 100644 --- a/Providers/FreeSql.Provider.Sqlite/FreeSql.Provider.Sqlite.csproj +++ b/Providers/FreeSql.Provider.Sqlite/FreeSql.Provider.Sqlite.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.667 + 3.2.668 diff --git a/Providers/FreeSql.Provider.SqliteCore/FreeSql.Provider.SqliteCore.csproj b/Providers/FreeSql.Provider.SqliteCore/FreeSql.Provider.SqliteCore.csproj index 410ebe2c..b61991bb 100644 --- a/Providers/FreeSql.Provider.SqliteCore/FreeSql.Provider.SqliteCore.csproj +++ b/Providers/FreeSql.Provider.SqliteCore/FreeSql.Provider.SqliteCore.csproj @@ -18,7 +18,7 @@ true key.snk false - 3.2.667 + 3.2.668