From f601d9b9e0afe62cfa838cca924e153fbdcd832c Mon Sep 17 00:00:00 2001
From: 2881099 <2881099@qq.com>
Date: Mon, 5 Sep 2022 13:08:22 +0800
Subject: [PATCH] AggregateRootRepository Boundary
---
.../AggregateRootBoundaryAttribute.cs | 6 +
FreeSql.Repository/AggregateRootModel.cs | 5 +
FreeSql.Repository/AggregateRootRepository.cs | 2 +-
.../AggregateRootRepositoryAsync.cs | 118 +++++++++++++++++-
.../AggregateRootRepositorySync.cs | 60 +--------
FreeSql.Repository/AggregateRootUtils.cs | 83 +++++-------
.../AggregateRootRepositoryTest2.cs | 18 ++-
7 files changed, 174 insertions(+), 118 deletions(-)
diff --git a/FreeSql.Repository/AggregateRootBoundaryAttribute.cs b/FreeSql.Repository/AggregateRootBoundaryAttribute.cs
index b45601ca..a2603b44 100644
--- a/FreeSql.Repository/AggregateRootBoundaryAttribute.cs
+++ b/FreeSql.Repository/AggregateRootBoundaryAttribute.cs
@@ -4,6 +4,12 @@ using System.Linq;
namespace FreeSql.DataAnnotations
{
+ ///
+ /// 设置 AggregateRootRepository 边界范围
+ /// 在边界范围之内的规则 :
+ /// 1、OneToOne/OneToMany/ManyToMany(中间表) 可以查询、可以增删改
+ /// 2、ManyToOne/ManyToMany外部表/PgArrayToMany 只可以查询,不支持增删改(会被忽略)
+ ///
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class AggregateRootBoundaryAttribute : Attribute
{
diff --git a/FreeSql.Repository/AggregateRootModel.cs b/FreeSql.Repository/AggregateRootModel.cs
index b0a02486..7bff61bd 100644
--- a/FreeSql.Repository/AggregateRootModel.cs
+++ b/FreeSql.Repository/AggregateRootModel.cs
@@ -3,10 +3,15 @@ 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.Repository/AggregateRootRepository.cs b/FreeSql.Repository/AggregateRootRepository.cs
index 7910b77e..ed043f80 100644
--- a/FreeSql.Repository/AggregateRootRepository.cs
+++ b/FreeSql.Repository/AggregateRootRepository.cs
@@ -40,7 +40,7 @@ namespace FreeSql
DisposeChildRepositorys();
_repository.FlushState();
FlushState();
- _boundaryName = name;
+ _boundaryName = string.Concat(name).Trim();
return this;
}
diff --git a/FreeSql.Repository/AggregateRootRepositoryAsync.cs b/FreeSql.Repository/AggregateRootRepositoryAsync.cs
index f6a94d82..1da92ee8 100644
--- a/FreeSql.Repository/AggregateRootRepositoryAsync.cs
+++ b/FreeSql.Repository/AggregateRootRepositoryAsync.cs
@@ -25,7 +25,7 @@ namespace FreeSql
var repos = new Dictionary();
try
{
- var ret = await InsertWithinBoundaryStaticAsync(_boundaryName, _repository, GetChildRepository, entitys, out var affrows, cancellationToken);
+ var ret = await InsertWithinBoundaryStaticAsync(_boundaryName, _repository, GetChildRepository, entitys, null, cancellationToken);
Attach(ret);
return ret;
}
@@ -35,9 +35,116 @@ namespace FreeSql
_repository.FlushState();
}
}
- Task> InsertWithinBoundaryStaticAsync(string boundaryName, IBaseRepository rootRepository, Func> getChildRepository, IEnumerable rootEntitys, out int affrows, CancellationToken cancellationToken) where T1 : class
+ async Task> InsertWithinBoundaryStaticAsync(string boundaryName, IBaseRepository rootRepository, Func> getChildRepository, IEnumerable rootEntitys, int[] affrows, CancellationToken cancellationToken) where T1 : class
{
- return Task.FromResult(InsertWithinBoundaryStatic(boundaryName, rootRepository, getChildRepository, rootEntitys, out affrows));
+ 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