diff --git a/FreeSql.Repository/AggregateRootBoundaryAttribute.cs b/FreeSql.Repository/AggregateRootBoundaryAttribute.cs
new file mode 100644
index 00000000..b45601ca
--- /dev/null
+++ b/FreeSql.Repository/AggregateRootBoundaryAttribute.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Linq;
+
+namespace FreeSql.DataAnnotations
+{
+
+ [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.Repository/AggregateRootRepository.cs b/FreeSql.Repository/AggregateRootRepository.cs
index 59a38c9d..7910b77e 100644
--- a/FreeSql.Repository/AggregateRootRepository.cs
+++ b/FreeSql.Repository/AggregateRootRepository.cs
@@ -1,4 +1,5 @@
using FreeSql.Extensions.EntityUtil;
+using FreeSql.Internal.Model;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -7,7 +8,12 @@ using System.Linq.Expressions;
namespace FreeSql
{
- public partial class AggregateRootRepository : IBaseRepository where TEntity : class
+ 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)
@@ -23,10 +29,21 @@ namespace FreeSql
public void Dispose()
{
DisposeChildRepositorys();
+ _repository.FlushState();
_repository.Dispose();
FlushState();
}
+ string _boundaryName = "";
+ public IBaseRepository ChangeBoundary(string name)
+ {
+ DisposeChildRepositorys();
+ _repository.FlushState();
+ FlushState();
+ _boundaryName = name;
+ return this;
+ }
+
public IFreeSql Orm => _repository.Orm;
public IUnitOfWork UnitOfWork { get => _repository.UnitOfWork; set => _repository.UnitOfWork = value; }
public DbContextOptions DbContextOptions
@@ -61,9 +78,26 @@ namespace FreeSql
Attach(item);
}
public IBaseRepository AttachOnlyPrimary(TEntity data) => _repository.AttachOnlyPrimary(data);
- public Dictionary CompareState(TEntity newdata) => _repository.CompareState(newdata);
+ 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();
}
@@ -117,7 +151,7 @@ namespace FreeSql
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(Orm, EntityType, data, state.Value);
+ AggregateRootUtils.MapEntityValue(_boundaryName, Orm, EntityType, data, state.Value);
return state;
}
bool? ExistsInStates(object data)
@@ -139,9 +173,9 @@ namespace FreeSql
/// 创建查询对象(纯净)
/// _
/// 聚合根内关系较复杂时,获取 Include/IncludeMany 字符串代码,方便二次开发
- /// string code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(fsql, typeof(Order))
+ /// string code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(null, fsql, typeof(Order))
///
- protected ISelect SelectDiy => _repository.Select;
+ protected ISelect SelectDiy => _repository.Select.TrackToList(SelectAggregateRootTracking);
///
/// 创建查询对象(递归包含 Include/IncludeMany 边界之内的导航属性)
///
@@ -151,7 +185,7 @@ namespace FreeSql
get
{
var query = _repository.Select.TrackToList(SelectAggregateRootTracking);
- query = AggregateRootUtils.GetAutoIncludeQuery(query);
+ query = AggregateRootUtils.GetAutoIncludeQuery(_boundaryName, query);
return query;
}
}
@@ -214,7 +248,8 @@ namespace FreeSql
// currentQuery.IncludeByPropertyName(navigateExpression);
// break;
// case TableRefType.PgArrayToMany:
- // case TableRefType.ManyToOne: //不属于聚合根
+ // break;
+ // case TableRefType.ManyToOne:
// break;
// }
// }
diff --git a/FreeSql.Repository/AggregateRootRepositoryAsync.cs b/FreeSql.Repository/AggregateRootRepositoryAsync.cs
index 1ecd4798..f6a94d82 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(_repository, GetChildRepository, entitys, out var affrows, cancellationToken);
+ var ret = await InsertWithinBoundaryStaticAsync(_boundaryName, _repository, GetChildRepository, entitys, out var affrows, cancellationToken);
Attach(ret);
return ret;
}
@@ -35,112 +35,9 @@ namespace FreeSql
_repository.FlushState();
}
}
- Task> InsertWithinBoundaryStaticAsync(IBaseRepository rootRepository, Func> getChildRepository, IEnumerable rootEntitys, out int affrows, CancellationToken cancellationToken) where T1 : class
+ Task> InsertWithinBoundaryStaticAsync(string boundaryName, IBaseRepository rootRepository, Func> getChildRepository, IEnumerable rootEntitys, out int affrows, CancellationToken cancellationToken) where T1 : class
{
- Dictionary> ignores = new Dictionary>();
- Dictionary> repos = new Dictionary>();
- var localAffrows = 0;
- try
- {
- return LocalInsertAsync(rootRepository, rootEntitys);
- }
- 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;
- }
- async Task> LocalInsertAsync(IBaseRepository repository, IEnumerable entitys) 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);
-
- 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;
- 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);
- }
- 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