AggregateRootRepository Boundary

This commit is contained in:
2881099 2022-09-04 23:06:44 +08:00
parent 5d12e1c436
commit 9db121d531
7 changed files with 291 additions and 185 deletions

View File

@ -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; }
/// <summary>
/// 边界是否终止
/// </summary>
public bool Break { get; set; }
/// <summary>
/// 边界是否终止向下探测
/// </summary>
public bool BreakThen { get; set; }
public AggregateRootBoundaryAttribute(string name)
{
this.Name = name;
}
public AggregateRootBoundaryAttribute()
{
}
}
}

View File

@ -1,4 +1,5 @@
using FreeSql.Extensions.EntityUtil; using FreeSql.Extensions.EntityUtil;
using FreeSql.Internal.Model;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@ -7,7 +8,12 @@ using System.Linq.Expressions;
namespace FreeSql namespace FreeSql
{ {
public partial class AggregateRootRepository<TEntity> : IBaseRepository<TEntity> where TEntity : class public interface IAggregateRootRepository<TEntity>: IBaseRepository<TEntity> where TEntity : class
{
IBaseRepository<TEntity> ChangeBoundary(string name);
}
public partial class AggregateRootRepository<TEntity> : IAggregateRootRepository<TEntity> where TEntity : class
{ {
readonly IBaseRepository<TEntity> _repository; readonly IBaseRepository<TEntity> _repository;
public AggregateRootRepository(IFreeSql fsql) public AggregateRootRepository(IFreeSql fsql)
@ -23,10 +29,21 @@ namespace FreeSql
public void Dispose() public void Dispose()
{ {
DisposeChildRepositorys(); DisposeChildRepositorys();
_repository.FlushState();
_repository.Dispose(); _repository.Dispose();
FlushState(); FlushState();
} }
string _boundaryName = "";
public IBaseRepository<TEntity> ChangeBoundary(string name)
{
DisposeChildRepositorys();
_repository.FlushState();
FlushState();
_boundaryName = name;
return this;
}
public IFreeSql Orm => _repository.Orm; public IFreeSql Orm => _repository.Orm;
public IUnitOfWork UnitOfWork { get => _repository.UnitOfWork; set => _repository.UnitOfWork = value; } public IUnitOfWork UnitOfWork { get => _repository.UnitOfWork; set => _repository.UnitOfWork = value; }
public DbContextOptions DbContextOptions public DbContextOptions DbContextOptions
@ -61,9 +78,26 @@ namespace FreeSql
Attach(item); Attach(item);
} }
public IBaseRepository<TEntity> AttachOnlyPrimary(TEntity data) => _repository.AttachOnlyPrimary(data); public IBaseRepository<TEntity> AttachOnlyPrimary(TEntity data) => _repository.AttachOnlyPrimary(data);
public Dictionary<string, object[]> CompareState(TEntity newdata) => _repository.CompareState(newdata); public Dictionary<string, object[]> 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<string, object[]>
{
["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() public void FlushState()
{ {
DisposeChildRepositorys();
_repository.FlushState(); _repository.FlushState();
_states.Clear(); _states.Clear();
} }
@ -117,7 +151,7 @@ namespace FreeSql
if (data == null) throw new ArgumentNullException(nameof(data)); if (data == null) throw new ArgumentNullException(nameof(data));
var key = Orm.GetEntityKeyString(EntityType, data, false); var key = Orm.GetEntityKeyString(EntityType, data, false);
var state = new EntityState((TEntity)EntityType.CreateInstanceGetDefaultValue(), key); 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; return state;
} }
bool? ExistsInStates(object data) bool? ExistsInStates(object data)
@ -139,9 +173,9 @@ namespace FreeSql
/// 创建查询对象(纯净)<para></para> /// 创建查询对象(纯净)<para></para>
/// _<para></para> /// _<para></para>
/// 聚合根内关系较复杂时,获取 Include/IncludeMany 字符串代码,方便二次开发<para></para> /// 聚合根内关系较复杂时,获取 Include/IncludeMany 字符串代码,方便二次开发<para></para>
/// string code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(fsql, typeof(Order)) /// string code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(null, fsql, typeof(Order))
/// </summary> /// </summary>
protected ISelect<TEntity> SelectDiy => _repository.Select; protected ISelect<TEntity> SelectDiy => _repository.Select.TrackToList(SelectAggregateRootTracking);
/// <summary> /// <summary>
/// 创建查询对象(递归包含 Include/IncludeMany 边界之内的导航属性) /// 创建查询对象(递归包含 Include/IncludeMany 边界之内的导航属性)
/// </summary> /// </summary>
@ -151,7 +185,7 @@ namespace FreeSql
get get
{ {
var query = _repository.Select.TrackToList(SelectAggregateRootTracking); var query = _repository.Select.TrackToList(SelectAggregateRootTracking);
query = AggregateRootUtils.GetAutoIncludeQuery(query); query = AggregateRootUtils.GetAutoIncludeQuery(_boundaryName, query);
return query; return query;
} }
} }
@ -214,7 +248,8 @@ namespace FreeSql
// currentQuery.IncludeByPropertyName(navigateExpression); // currentQuery.IncludeByPropertyName(navigateExpression);
// break; // break;
// case TableRefType.PgArrayToMany: // case TableRefType.PgArrayToMany:
// case TableRefType.ManyToOne: //不属于聚合根 // break;
// case TableRefType.ManyToOne:
// break; // break;
// } // }
// } // }

View File

@ -25,7 +25,7 @@ namespace FreeSql
var repos = new Dictionary<Type, object>(); var repos = new Dictionary<Type, object>();
try 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); Attach(ret);
return ret; return ret;
} }
@ -35,112 +35,9 @@ namespace FreeSql
_repository.FlushState(); _repository.FlushState();
} }
} }
Task<List<T1>> InsertWithinBoundaryStaticAsync<T1>(IBaseRepository<T1> rootRepository, Func<Type, IBaseRepository<object>> getChildRepository, IEnumerable<T1> rootEntitys, out int affrows, CancellationToken cancellationToken) where T1 : class Task<List<T1>> InsertWithinBoundaryStaticAsync<T1>(string boundaryName, IBaseRepository<T1> rootRepository, Func<Type, IBaseRepository<object>> getChildRepository, IEnumerable<T1> rootEntitys, out int affrows, CancellationToken cancellationToken) where T1 : class
{ {
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>(); return Task.FromResult(InsertWithinBoundaryStatic(boundaryName, rootRepository, getChildRepository, rootEntitys, out affrows));
Dictionary<Type, IBaseRepository<object>> repos = new Dictionary<Type, IBaseRepository<object>>();
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<string, bool>());
stateKeys.Add(stateKey, true);
}
return true;
}
if (stateKeys.ContainsKey(stateKey) == false)
{
if (isadd) stateKeys.Add(stateKey, true);
return true;
}
return false;
}
async Task<List<T2>> LocalInsertAsync<T2>(IBaseRepository<T2> repository, IEnumerable<T2> 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<object>();
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);
}
break;
case TableRefType.ManyToMany:
var mtmMidList = new List<object>();
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);
}
break;
case TableRefType.PgArrayToMany:
break;
}
}
return ret;
}
} }
#endregion #endregion
@ -178,7 +75,7 @@ namespace FreeSql
{ {
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false); var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以更新数据 {Orm.GetEntityString(EntityType, entity)}"); if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以更新数据 {Orm.GetEntityString(EntityType, entity)}");
AggregateRootUtils.CompareEntityValue(Orm, EntityType, state.Value, entity, null, tracking); AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, state.Value, entity, null, tracking);
} }
foreach (var entity in entitys) foreach (var entity in entitys)
Attach(entity); Attach(entity);
@ -202,7 +99,7 @@ namespace FreeSql
foreach (var entity in entitys) foreach (var entity in entitys)
{ {
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false); var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
AggregateRootUtils.CompareEntityValue(Orm, EntityType, entity, null, null, tracking); AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, entity, null, null, tracking);
_states.Remove(stateKey); _states.Remove(stateKey);
} }
var affrows = 0; var affrows = 0;
@ -227,7 +124,7 @@ namespace FreeSql
var tracking = new AggregateRootTrackingChangeInfo(); var tracking = new AggregateRootTrackingChangeInfo();
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false); var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以保存数据 {Orm.GetEntityString(EntityType, entity)}"); if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以保存数据 {Orm.GetEntityString(EntityType, entity)}");
AggregateRootUtils.CompareEntityValue(Orm, EntityType, state.Value, entity, propertyName, tracking); AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, state.Value, entity, propertyName, tracking);
Attach(entity); //应该只存储 propertyName 内容 Attach(entity); //应该只存储 propertyName 内容
await SaveTrackingChangeAsync(tracking, cancellationToken); await SaveTrackingChangeAsync(tracking, cancellationToken);
} }
@ -241,7 +138,7 @@ namespace FreeSql
foreach (var il in insertLogDict) foreach (var il in insertLogDict)
{ {
var repo = GetChildRepository(il.Key); var repo = GetChildRepository(il.Key);
await InsertWithinBoundaryStaticAsync(repo, GetChildRepository, il.Value, out var affrowsOut, cancellationToken); await InsertWithinBoundaryStaticAsync(_boundaryName, repo, GetChildRepository, il.Value, out var affrowsOut, cancellationToken);
affrows += affrowsOut; affrows += affrowsOut;
} }

View File

@ -33,7 +33,7 @@ namespace FreeSql
_statesEditing.AddOrUpdate(key, k => CreateEntityState(item), (k, ov) => _statesEditing.AddOrUpdate(key, k => CreateEntityState(item), (k, ov) =>
{ {
AggregateRootUtils.MapEntityValue(Orm, EntityType, item, ov.Value); AggregateRootUtils.MapEntityValue(_boundaryName, Orm, EntityType, item, ov.Value);
ov.Time = DateTime.Now; ov.Time = DateTime.Now;
return ov; return ov;
}); });
@ -57,10 +57,10 @@ namespace FreeSql
continue; continue;
} }
_states[key] = state; _states[key] = state;
AggregateRootUtils.CompareEntityValue(Orm, EntityType, state.Value, item, null, tracking); AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, state.Value, item, null, tracking);
} }
foreach (var item in _statesEditing.Values.OrderBy(a => a.Time)) foreach (var item in _statesEditing.Values.OrderBy(a => a.Time))
AggregateRootUtils.CompareEntityValue(Orm, EntityType, item, null, null, tracking); AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, item, null, null, tracking);
return SaveTrackingChange(tracking); return SaveTrackingChange(tracking);
} }
@ -79,7 +79,7 @@ namespace FreeSql
var repos = new Dictionary<Type, object>(); var repos = new Dictionary<Type, object>();
try try
{ {
var ret = InsertWithinBoundaryStatic(_repository, GetChildRepository, entitys, out var affrows); var ret = InsertWithinBoundaryStatic(_boundaryName, _repository, GetChildRepository, entitys, out var affrows);
Attach(ret); Attach(ret);
return ret; return ret;
} }
@ -89,13 +89,13 @@ namespace FreeSql
_repository.FlushState(); _repository.FlushState();
} }
} }
static List<T1> InsertWithinBoundaryStatic<T1>(IBaseRepository<T1> rootRepository, Func<Type, IBaseRepository<object>> getChildRepository, IEnumerable<T1> rootEntitys, out int affrows) where T1 : class { static List<T1> InsertWithinBoundaryStatic<T1>(string boundaryName, IBaseRepository<T1> rootRepository, Func<Type, IBaseRepository<object>> getChildRepository, IEnumerable<T1> rootEntitys, out int affrows) where T1 : class {
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>(); Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
Dictionary<Type, IBaseRepository<object>> repos = new Dictionary<Type, IBaseRepository<object>>(); Dictionary<Type, IBaseRepository<object>> repos = new Dictionary<Type, IBaseRepository<object>>();
var localAffrows = 0; var localAffrows = 0;
try try
{ {
return LocalInsert(rootRepository, rootEntitys); return LocalInsert(rootRepository, rootEntitys, true);
} }
finally finally
{ {
@ -122,7 +122,7 @@ namespace FreeSql
} }
return false; return false;
} }
List<T2> LocalInsert<T2>(IBaseRepository<T2> repository, IEnumerable<T2> entitys) where T2 : class List<T2> LocalInsert<T2>(IBaseRepository<T2> repository, IEnumerable<T2> entitys, bool cascade) where T2 : class
{ {
var table = repository.Orm.CodeFirst.GetTableByEntity(repository.EntityType); var table = repository.Orm.CodeFirst.GetTableByEntity(repository.EntityType);
if (table.Primarys.Any(col => col.Attribute.IsIdentity)) if (table.Primarys.Any(col => col.Attribute.IsIdentity))
@ -130,15 +130,76 @@ namespace FreeSql
foreach (var entity in entitys) foreach (var entity in entitys)
repository.Orm.ClearEntityPrimaryValueWithIdentity(repository.EntityType, entity); repository.Orm.ClearEntityPrimaryValueWithIdentity(repository.EntityType, entity);
} }
if (cascade)
{
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.ManyToMany:
if (boundaryAttr?.Break == false)
{
var mtmList = entitys.Select(entity =>
{
var mtmEach = table.GetPropertyValue(entity, prop.Name) as IEnumerable;
if (mtmEach == null) return null;
var mtmItems = new List<object>();
foreach (var mtmItem in mtmEach)
{
if (LocalCanInsert(tbref.RefEntityType, mtmItem, false) == false) continue;
mtmItems.Add(mtmItem);
}
return mtmItems;
}).Where(entity => entity != null).SelectMany(entity => entity).ToArray();
if (mtmList.Any())
{
var repo = getChildRepository(tbref.RefEntityType);
LocalInsert(repo, mtmList, boundaryAttr?.BreakThen == false);
}
}
break;
case TableRefType.PgArrayToMany:
break;
case TableRefType.ManyToOne:
if (boundaryAttr?.Break == false)
{
var mtoList = entitys.Select(entity =>
{
var mtoItem = table.GetPropertyValue(entity, prop.Name);
if (LocalCanInsert(tbref.RefEntityType, mtoItem, false) == false) return null;
return NativeTuple.Create(entity, mtoItem);
}).Where(entity => entity != null).ToArray();
if (mtoList.Any())
{
var repo = getChildRepository(tbref.RefEntityType);
LocalInsert(repo, mtoList.Select(a => a.Item2), boundaryAttr?.BreakThen != true);
foreach (var mtoItem in mtoList)
AggregateRootUtils.SetNavigateRelationshipValue(repository.Orm, tbref, table.Type, mtoItem.Item1, mtoItem.Item2);
}
}
break;
}
}
}
var ret = repository.Insert(entitys); var ret = repository.Insert(entitys);
localAffrows += ret.Count; localAffrows += ret.Count;
foreach (var entity in entitys) LocalCanInsert(repository.EntityType, entity, true); 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)) foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key))
{ {
var tbref = tr.Value; var tbref = tr.Value;
if (tbref.Exception != null) continue; if (tbref.Exception != null) continue;
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) 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) switch (tbref.RefType)
{ {
case TableRefType.OneToOne: case TableRefType.OneToOne:
@ -152,7 +213,7 @@ namespace FreeSql
if (otoList.Any()) if (otoList.Any())
{ {
var repo = getChildRepository(tbref.RefEntityType); var repo = getChildRepository(tbref.RefEntityType);
LocalInsert(repo, otoList); LocalInsert(repo, otoList, boundaryAttr?.BreakThen != true);
} }
break; break;
case TableRefType.OneToMany: case TableRefType.OneToMany:
@ -172,7 +233,7 @@ namespace FreeSql
if (otmList.Any()) if (otmList.Any())
{ {
var repo = getChildRepository(tbref.RefEntityType); var repo = getChildRepository(tbref.RefEntityType);
LocalInsert(repo, otmList); LocalInsert(repo, otmList, boundaryAttr?.BreakThen != true);
} }
break; break;
case TableRefType.ManyToMany: case TableRefType.ManyToMany:
@ -185,10 +246,11 @@ namespace FreeSql
if (mtmMidList.Any()) if (mtmMidList.Any())
{ {
var repo = getChildRepository(tbref.RefMiddleEntityType); var repo = getChildRepository(tbref.RefMiddleEntityType);
LocalInsert(repo, mtmMidList); LocalInsert(repo, mtmMidList, false);
} }
break; break;
case TableRefType.PgArrayToMany: case TableRefType.PgArrayToMany:
case TableRefType.ManyToOne: //在插入前处理
break; break;
} }
} }
@ -231,7 +293,7 @@ namespace FreeSql
{ {
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false); var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以更新数据 {Orm.GetEntityString(EntityType, entity)}"); if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以更新数据 {Orm.GetEntityString(EntityType, entity)}");
AggregateRootUtils.CompareEntityValue(Orm, EntityType, state.Value, entity, null, tracking); AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, state.Value, entity, null, tracking);
} }
foreach (var entity in entitys) foreach (var entity in entitys)
Attach(entity); Attach(entity);
@ -254,7 +316,7 @@ namespace FreeSql
foreach (var entity in entitys) foreach (var entity in entitys)
{ {
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false); var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
AggregateRootUtils.CompareEntityValue(Orm, EntityType, entity, null, null, tracking); AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, entity, null, null, tracking);
_states.Remove(stateKey); _states.Remove(stateKey);
} }
var affrows = 0; var affrows = 0;
@ -279,7 +341,7 @@ namespace FreeSql
var tracking = new AggregateRootTrackingChangeInfo(); var tracking = new AggregateRootTrackingChangeInfo();
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false); var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以保存数据 {Orm.GetEntityString(EntityType, entity)}"); if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以保存数据 {Orm.GetEntityString(EntityType, entity)}");
AggregateRootUtils.CompareEntityValue(Orm, EntityType, state.Value, entity, propertyName, tracking); AggregateRootUtils.CompareEntityValue(_boundaryName, Orm, EntityType, state.Value, entity, propertyName, tracking);
Attach(entity); //应该只存储 propertyName 内容 Attach(entity); //应该只存储 propertyName 内容
SaveTrackingChange(tracking); SaveTrackingChange(tracking);
} }
@ -293,7 +355,7 @@ namespace FreeSql
foreach (var il in insertLogDict) foreach (var il in insertLogDict)
{ {
var repo = GetChildRepository(il.Key); var repo = GetChildRepository(il.Key);
InsertWithinBoundaryStatic(repo, GetChildRepository, il.Value, out var affrowsOut); InsertWithinBoundaryStatic(_boundaryName, repo, GetChildRepository, il.Value, out var affrowsOut);
affrows += affrowsOut; affrows += affrowsOut;
} }

View File

@ -1,4 +1,5 @@
using FreeSql; using FreeSql;
using FreeSql.DataAnnotations;
using FreeSql.Extensions.EntityUtil; using FreeSql.Extensions.EntityUtil;
using FreeSql.Internal; using FreeSql.Internal;
using FreeSql.Internal.CommonProvider; using FreeSql.Internal.CommonProvider;
@ -15,15 +16,23 @@ using System.Text;
namespace FreeSql namespace FreeSql
{ {
public static class AggregateRootUtils public class AggregateRootUtils
{ {
public static void CompareEntityValue(IFreeSql fsql, Type rootEntityType, object rootEntityBefore, object rootEntityAfter, string rootNavigatePropertyName, AggregateRootTrackingChangeInfo tracking) public static AggregateRootBoundaryAttribute GetPropertyBoundaryAttribute(PropertyInfo prop, string boundaryName)
{
if (string.IsNullOrWhiteSpace(boundaryName)) return null;
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 == boundaryName).FirstOrDefault();
}
public static void CompareEntityValue(string boundaryName, IFreeSql fsql, Type rootEntityType, object rootEntityBefore, object rootEntityAfter, string rootNavigatePropertyName, AggregateRootTrackingChangeInfo tracking)
{ {
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>(); Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
LocalCompareEntityValue(rootEntityType, rootEntityBefore, rootEntityAfter, rootNavigatePropertyName); LocalCompareEntityValue(rootEntityType, rootEntityBefore, rootEntityAfter, rootNavigatePropertyName, true);
ignores.Clear(); ignores.Clear();
void LocalCompareEntityValue(Type entityType, object entityBefore, object entityAfter, string navigatePropertyName) void LocalCompareEntityValue(Type entityType, object entityBefore, object entityAfter, string navigatePropertyName, bool cascade)
{ {
if (entityType == null) entityType = entityBefore?.GetType() ?? entityAfter?.GetType(); if (entityType == null) entityType = entityBefore?.GetType() ?? entityAfter?.GetType();
@ -53,7 +62,7 @@ namespace FreeSql
if (entityBefore != null && entityAfter == null) if (entityBefore != null && entityAfter == null)
{ {
tracking.DeleteLog.Add(NativeTuple.Create(entityType, new[] { entityBefore })); tracking.DeleteLog.Add(NativeTuple.Create(entityType, new[] { entityBefore }));
NavigateReader(fsql, entityType, entityBefore, (path, tr, ct, stackvs) => NavigateReader(boundaryName, fsql, entityType, entityBefore, (path, tr, ct, stackvs) =>
{ {
var dellist = stackvs.Last() as object[] ?? new[] { stackvs.Last() }; var dellist = stackvs.Last() as object[] ?? new[] { stackvs.Last() };
tracking.DeleteLog.Add(NativeTuple.Create(ct, dellist)); tracking.DeleteLog.Add(NativeTuple.Create(ct, dellist));
@ -74,14 +83,16 @@ namespace FreeSql
continue; continue;
} }
} }
if (changes.Any()) if (changes.Any()) tracking.UpdateLog.Add(NativeTuple.Create(entityType, entityBefore, entityAfter, changes));
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)) foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key))
{ {
var tbref = tr.Value; var tbref = tr.Value;
if (tbref.Exception != null) continue; if (tbref.Exception != null) continue;
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) 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; if (navigatePropertyName != null && prop.Name != navigatePropertyName) continue;
var propvalBefore = table.GetPropertyValue(entityBefore, prop.Name); var propvalBefore = table.GetPropertyValue(entityBefore, prop.Name);
var propvalAfter = table.GetPropertyValue(entityAfter, prop.Name); var propvalAfter = table.GetPropertyValue(entityAfter, prop.Name);
@ -90,27 +101,35 @@ namespace FreeSql
case TableRefType.OneToOne: case TableRefType.OneToOne:
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityBefore, propvalBefore); SetNavigateRelationshipValue(fsql, tbref, table.Type, entityBefore, propvalBefore);
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityAfter, propvalAfter); SetNavigateRelationshipValue(fsql, tbref, table.Type, entityAfter, propvalAfter);
LocalCompareEntityValue(tbref.RefEntityType, propvalBefore, propvalAfter, null); LocalCompareEntityValue(tbref.RefEntityType, propvalBefore, propvalAfter, null, boundaryAttr?.BreakThen != true);
break; break;
case TableRefType.OneToMany: case TableRefType.OneToMany:
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityBefore, propvalBefore); SetNavigateRelationshipValue(fsql, tbref, table.Type, entityBefore, propvalBefore);
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityAfter, propvalAfter); SetNavigateRelationshipValue(fsql, tbref, table.Type, entityAfter, propvalAfter);
LocalCompareEntityValueCollection(tbref, propvalBefore as IEnumerable, propvalAfter as IEnumerable); LocalCompareEntityValueCollection(tbref.RefEntityType, propvalBefore as IEnumerable, propvalAfter as IEnumerable, boundaryAttr?.BreakThen != true);
break; break;
case TableRefType.ManyToMany: case TableRefType.ManyToMany:
var middleValuesBefore = GetManyToManyObjects(fsql, table, tbref, entityBefore, prop); var middleValuesBefore = GetManyToManyObjects(fsql, table, tbref, entityBefore, prop);
var middleValuesAfter = GetManyToManyObjects(fsql, table, tbref, entityAfter, prop); var middleValuesAfter = GetManyToManyObjects(fsql, table, tbref, entityAfter, prop);
LocalCompareEntityValueCollection(tbref, middleValuesBefore as IEnumerable, middleValuesAfter as IEnumerable); LocalCompareEntityValueCollection(tbref.RefMiddleEntityType, middleValuesBefore as IEnumerable, middleValuesAfter as IEnumerable, false);
if (boundaryAttr?.BreakThen == false)
LocalCompareEntityValueCollection(tbref.RefEntityType, propvalBefore as IEnumerable, propvalAfter as IEnumerable, boundaryAttr?.BreakThen == false);
break; break;
case TableRefType.PgArrayToMany: case TableRefType.PgArrayToMany:
case TableRefType.ManyToOne: //不属于聚合根 break;
case TableRefType.ManyToOne:
if (boundaryAttr?.BreakThen == false)
{
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityBefore, propvalBefore);
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityAfter, propvalAfter);
LocalCompareEntityValue(tbref.RefEntityType, propvalBefore, propvalAfter, null, boundaryAttr?.BreakThen == false);
}
break; break;
} }
} }
} }
void LocalCompareEntityValueCollection(TableRef tbref, IEnumerable collectionBefore, IEnumerable collectionAfter) void LocalCompareEntityValueCollection(Type elementType, IEnumerable collectionBefore, IEnumerable collectionAfter, bool cascade)
{ {
var elementType = tbref.RefType == TableRefType.ManyToMany ? tbref.RefMiddleEntityType : tbref.RefEntityType;
if (collectionBefore == null && collectionAfter == null) return; if (collectionBefore == null && collectionAfter == null) return;
if (collectionBefore == null && collectionAfter != null) if (collectionBefore == null && collectionAfter != null)
{ {
@ -154,7 +173,7 @@ namespace FreeSql
{ {
var value = dictBefore[key]; var value = dictBefore[key];
tracking.DeleteLog.Add(NativeTuple.Create(elementType, new[] { value })); tracking.DeleteLog.Add(NativeTuple.Create(elementType, new[] { value }));
NavigateReader(fsql, elementType, value, (path, tr, ct, stackvs) => NavigateReader(boundaryName, fsql, elementType, value, (path, tr, ct, stackvs) =>
{ {
var dellist = stackvs.Last() as object[] ?? new[] { stackvs.Last() }; var dellist = stackvs.Last() as object[] ?? new[] { stackvs.Last() };
tracking.DeleteLog.Add(NativeTuple.Create(ct, dellist)); tracking.DeleteLog.Add(NativeTuple.Create(ct, dellist));
@ -171,7 +190,7 @@ namespace FreeSql
} }
} }
foreach (var key in dictBefore.Keys) foreach (var key in dictBefore.Keys)
LocalCompareEntityValue(elementType, dictBefore[key], dictAfter[key], null); LocalCompareEntityValue(elementType, dictBefore[key], dictAfter[key], null, cascade);
} }
} }
@ -259,7 +278,7 @@ namespace FreeSql
return object.Equals(propvalBefore, propvalAfter); return object.Equals(propvalBefore, propvalAfter);
} }
public static void NavigateReader(IFreeSql fsql, Type rootType, object rootEntity, Action<string, TableRef, Type, List<object>> callback) public static void NavigateReader(string boundaryName, IFreeSql fsql, Type rootType, object rootEntity, Action<string, TableRef, Type, List<object>> callback)
{ {
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>(); Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
var statckPath = new Stack<string>(); var statckPath = new Stack<string>();
@ -286,6 +305,8 @@ namespace FreeSql
var tbref = tr.Value; var tbref = tr.Value;
if (tbref.Exception != null) continue; if (tbref.Exception != null) continue;
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) 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) switch (tbref.RefType)
{ {
case TableRefType.OneToOne: case TableRefType.OneToOne:
@ -295,49 +316,70 @@ namespace FreeSql
stackValues.Add(propval); stackValues.Add(propval);
SetNavigateRelationshipValue(fsql, tbref, table.Type, entity, propval); SetNavigateRelationshipValue(fsql, tbref, table.Type, entity, propval);
callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefEntityType, stackValues); callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefEntityType, stackValues);
LocalNavigateReader(tbref.RefEntityType, propval); if (boundaryAttr?.BreakThen != true)
LocalNavigateReader(tbref.RefEntityType, propval);
stackValues.RemoveAt(stackValues.Count - 1); stackValues.RemoveAt(stackValues.Count - 1);
statckPath.Pop(); statckPath.Pop();
break; break;
case TableRefType.OneToMany: case TableRefType.OneToMany:
var propvalOtm = table.GetPropertyValue(entity, prop.Name); var propvalOtm = table.GetPropertyValue(entity, prop.Name) as IEnumerable;
if (propvalOtm == null) continue; if (propvalOtm == null) continue;
SetNavigateRelationshipValue(fsql, tbref, table.Type, entity, propvalOtm); SetNavigateRelationshipValue(fsql, tbref, table.Type, entity, propvalOtm);
var propvalOtmList = new List<object>(); var propvalOtmList = new List<object>();
foreach (var val in propvalOtm as IEnumerable) foreach (var val in propvalOtm)
propvalOtmList.Add(val); propvalOtmList.Add(val);
statckPath.Push($"{prop.Name}[]"); statckPath.Push($"{prop.Name}[]");
stackValues.Add(propvalOtmList.ToArray()); stackValues.Add(propvalOtmList.ToArray());
callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefEntityType, stackValues); callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefEntityType, stackValues);
foreach (var val in propvalOtm as IEnumerable) if (boundaryAttr?.BreakThen != true)
LocalNavigateReader(tbref.RefEntityType, val); foreach (var val in propvalOtm)
LocalNavigateReader(tbref.RefEntityType, val);
stackValues.RemoveAt(stackValues.Count - 1); stackValues.RemoveAt(stackValues.Count - 1);
statckPath.Pop(); statckPath.Pop();
break; break;
case TableRefType.ManyToMany: case TableRefType.ManyToMany:
var propvalMtm = table.GetPropertyValue(entity, prop.Name) as IEnumerable;
if (propvalMtm == null) continue;
var middleValues = GetManyToManyObjects(fsql, table, tbref, entity, prop).ToArray(); var middleValues = GetManyToManyObjects(fsql, table, tbref, entity, prop).ToArray();
if (middleValues == null) continue; if (middleValues == null) continue;
statckPath.Push($"{prop.Name}[]"); statckPath.Push($"{prop.Name}[]");
stackValues.Add(middleValues); stackValues.Add(middleValues);
callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefMiddleEntityType, stackValues); callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefMiddleEntityType, stackValues);
if (boundaryAttr?.BreakThen == false)
foreach (var val in propvalMtm)
LocalNavigateReader(tbref.RefEntityType, val);
stackValues.RemoveAt(stackValues.Count - 1); stackValues.RemoveAt(stackValues.Count - 1);
statckPath.Pop(); statckPath.Pop();
break; break;
case TableRefType.PgArrayToMany: case TableRefType.PgArrayToMany:
case TableRefType.ManyToOne: //不属于聚合根 break;
case TableRefType.ManyToOne:
if (boundaryAttr?.Break == false)
{
var propvalMto = table.GetPropertyValue(entity, prop.Name);
if (propvalMto == null) continue;
statckPath.Push(prop.Name);
stackValues.Add(propvalMto);
SetNavigateRelationshipValue(fsql, tbref, table.Type, entity, propvalMto);
callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefEntityType, stackValues);
if (boundaryAttr?.BreakThen == false)
LocalNavigateReader(tbref.RefEntityType, propvalMto);
stackValues.RemoveAt(stackValues.Count - 1);
statckPath.Pop();
}
break; break;
} }
} }
} }
} }
public static void MapEntityValue(IFreeSql fsql, Type rootEntityType, object rootEntityFrom, object rootEntityTo) public static void MapEntityValue(string boundaryName, IFreeSql fsql, Type rootEntityType, object rootEntityFrom, object rootEntityTo)
{ {
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>(); Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
LocalMapEntityValue(rootEntityType, rootEntityFrom, rootEntityTo); LocalMapEntityValue(rootEntityType, rootEntityFrom, rootEntityTo, true);
ignores.Clear(); ignores.Clear();
void LocalMapEntityValue(Type entityType, object entityFrom, object entityTo) void LocalMapEntityValue(Type entityType, object entityFrom, object entityTo, bool cascade)
{ {
if (entityFrom == null || entityTo == null) return; if (entityFrom == null || entityTo == null) return;
if (entityType == null) entityType = entityFrom?.GetType() ?? entityTo?.GetType(); if (entityType == null) entityType = entityFrom?.GetType() ?? entityTo?.GetType();
@ -357,8 +399,11 @@ namespace FreeSql
table.SetPropertyValue(entityTo, prop.Name, table.GetPropertyValue(entityFrom, prop.Name)); table.SetPropertyValue(entityTo, prop.Name, table.GetPropertyValue(entityFrom, prop.Name));
continue; continue;
} }
if (cascade == false) continue;
var tbref = table.GetTableRef(prop.Name, false); var tbref = table.GetTableRef(prop.Name, false);
if (tbref == null) continue; if (tbref == null) continue;
var boundaryAttr = GetPropertyBoundaryAttribute(prop, boundaryName);
if (boundaryAttr?.Break == true) continue;
var propvalFrom = EntityUtilExtensions.GetEntityValueWithPropertyName(fsql, entityType, entityFrom, prop.Name); var propvalFrom = EntityUtilExtensions.GetEntityValueWithPropertyName(fsql, entityType, entityFrom, prop.Name);
if (propvalFrom == null) if (propvalFrom == null)
{ {
@ -370,18 +415,26 @@ namespace FreeSql
case TableRefType.OneToOne: case TableRefType.OneToOne:
var propvalTo = tbref.RefEntityType.CreateInstanceGetDefaultValue(); var propvalTo = tbref.RefEntityType.CreateInstanceGetDefaultValue();
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityFrom, propvalFrom); SetNavigateRelationshipValue(fsql, tbref, table.Type, entityFrom, propvalFrom);
LocalMapEntityValue(tbref.RefEntityType, propvalFrom, propvalTo); LocalMapEntityValue(tbref.RefEntityType, propvalFrom, propvalTo, boundaryAttr?.BreakThen != true);
EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, propvalTo); EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, propvalTo);
break; break;
case TableRefType.OneToMany: case TableRefType.OneToMany:
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityFrom, propvalFrom); SetNavigateRelationshipValue(fsql, tbref, table.Type, entityFrom, propvalFrom);
LocalMapEntityValueCollection(entityType, entityFrom, entityTo, tbref, propvalFrom as IEnumerable, prop, true); LocalMapEntityValueCollection(entityType, entityFrom, entityTo, tbref, propvalFrom as IEnumerable, prop, boundaryAttr?.BreakThen != true);
break; break;
case TableRefType.ManyToMany: case TableRefType.ManyToMany:
LocalMapEntityValueCollection(entityType, entityFrom, entityTo, tbref, propvalFrom as IEnumerable, prop, false); LocalMapEntityValueCollection(entityType, entityFrom, entityTo, tbref, propvalFrom as IEnumerable, prop, boundaryAttr?.BreakThen == false);
break; break;
case TableRefType.PgArrayToMany: case TableRefType.PgArrayToMany:
case TableRefType.ManyToOne: //不属于聚合根 break;
case TableRefType.ManyToOne:
if (boundaryAttr?.Break == false)
{
var propvalTo2 = tbref.RefEntityType.CreateInstanceGetDefaultValue();
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityFrom, propvalFrom);
LocalMapEntityValue(tbref.RefEntityType, propvalFrom, propvalTo2, boundaryAttr?.BreakThen == false);
EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, propvalTo2);
}
break; break;
} }
} }
@ -393,8 +446,7 @@ namespace FreeSql
foreach (var fromItem in propvalFrom) foreach (var fromItem in propvalFrom)
{ {
var toItem = tbref.RefEntityType.CreateInstanceGetDefaultValue(); var toItem = tbref.RefEntityType.CreateInstanceGetDefaultValue();
if (cascade) LocalMapEntityValue(tbref.RefEntityType, fromItem, toItem); LocalMapEntityValue(tbref.RefEntityType, fromItem, toItem, cascade);
else EntityUtilExtensions.MapEntityValue(fsql, tbref.RefEntityType, fromItem, toItem);
propvalToIList.Add(toItem); propvalToIList.Add(toItem);
} }
var propvalType = prop.PropertyType.GetGenericTypeDefinition(); var propvalType = prop.PropertyType.GetGenericTypeDefinition();
@ -409,12 +461,14 @@ namespace FreeSql
} }
} }
static ConcurrentDictionary<Type, ConcurrentDictionary<Type, Action<ISelect0>>> _dicGetAutoIncludeQuery = new ConcurrentDictionary<Type, ConcurrentDictionary<Type, Action<ISelect0>>>(); static ConcurrentDictionary<string, ConcurrentDictionary<Type, ConcurrentDictionary<Type, Action<ISelect0>>>> _dicGetAutoIncludeQuery = new ConcurrentDictionary<string, ConcurrentDictionary<Type, ConcurrentDictionary<Type, Action<ISelect0>>>>();
public static ISelect<TEntity> GetAutoIncludeQuery<TEntity>(ISelect<TEntity> select) public static ISelect<TEntity> GetAutoIncludeQuery<TEntity>(string boundaryName, ISelect<TEntity> select)
{ {
var select0p = select as Select0Provider; var select0p = select as Select0Provider;
var table0Type = select0p._tables[0].Table.Type; var table0Type = select0p._tables[0].Table.Type;
var func = _dicGetAutoIncludeQuery.GetOrAdd(typeof(TEntity), t => new ConcurrentDictionary<Type, Action<ISelect0>>()).GetOrAdd(table0Type, t => var func = _dicGetAutoIncludeQuery.GetOrAdd(boundaryName ?? "", bn => new ConcurrentDictionary<Type, ConcurrentDictionary<Type, Action<ISelect0>>>())
.GetOrAdd(typeof(TEntity), t => new ConcurrentDictionary<Type, Action<ISelect0>>())
.GetOrAdd(table0Type, t =>
{ {
var parmExp1 = Expression.Parameter(typeof(ISelect0)); var parmExp1 = Expression.Parameter(typeof(ISelect0));
var parmNavigateParameterExp = Expression.Parameter(typeof(TEntity), "a"); var parmNavigateParameterExp = Expression.Parameter(typeof(TEntity), "a");
@ -435,6 +489,8 @@ namespace FreeSql
var tbref = tr.Value; var tbref = tr.Value;
if (tbref.Exception != null) continue; if (tbref.Exception != null) continue;
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) 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); Expression navigateExp = Expression.MakeMemberAccess(navigatePathExp, prop);
//var lambdaAlias = (char)((byte)'a' + (depth - 1)); //var lambdaAlias = (char)((byte)'a' + (depth - 1));
switch (tbref.RefType) switch (tbref.RefType)
@ -442,17 +498,24 @@ namespace FreeSql
case TableRefType.OneToOne: case TableRefType.OneToOne:
if (ignores.Any(a => a == tbref.RefEntityType)) break; if (ignores.Any(a => a == tbref.RefEntityType)) break;
LocalInclude(tbref, navigateExp); LocalInclude(tbref, navigateExp);
queryExp = LocalGetAutoIncludeQuery(queryExp, depth, tbref.RefEntityType, navigateParameterExp, navigateExp, ignores); if (boundaryAttr?.BreakThen != true)
queryExp = LocalGetAutoIncludeQuery(queryExp, depth, tbref.RefEntityType, navigateParameterExp, navigateExp, ignores);
break; break;
case TableRefType.OneToMany: case TableRefType.OneToMany:
LocalIncludeMany(tbref, navigateExp, true); LocalIncludeMany(tbref, navigateExp, boundaryAttr?.BreakThen != true);
break; break;
case TableRefType.ManyToMany: case TableRefType.ManyToMany:
LocalIncludeMany(tbref, navigateExp, false); LocalIncludeMany(tbref, navigateExp, boundaryAttr?.BreakThen == false);
break; break;
case TableRefType.PgArrayToMany: case TableRefType.PgArrayToMany:
break; break;
case TableRefType.ManyToOne: //不属于聚合根 case TableRefType.ManyToOne:
if (boundaryAttr?.Break == false)
{
LocalInclude(tbref, navigateExp);
if (boundaryAttr?.BreakThen == false)
queryExp = LocalGetAutoIncludeQuery(queryExp, depth, tbref.RefEntityType, navigateParameterExp, navigateExp, ignores);
}
break; break;
} }
} }
@ -482,7 +545,8 @@ namespace FreeSql
} }
} }
} }
public static string GetAutoIncludeQueryStaicCode(IFreeSql fsql, Type rootEntityType)
public static string GetAutoIncludeQueryStaicCode(string boundaryName, IFreeSql fsql, Type rootEntityType)
{ {
return $"//fsql.Select<{rootEntityType.Name}>()\r\nSelectDiy{LocalGetAutoIncludeQueryStaicCode(1, rootEntityType, "", new Stack<Type>())}"; return $"//fsql.Select<{rootEntityType.Name}>()\r\nSelectDiy{LocalGetAutoIncludeQueryStaicCode(1, rootEntityType, "", new Stack<Type>())}";
string LocalGetAutoIncludeQueryStaicCode(int depth, Type entityType, string navigatePath, Stack<Type> ignores) string LocalGetAutoIncludeQueryStaicCode(int depth, Type entityType, string navigatePath, Stack<Type> ignores)
@ -497,6 +561,9 @@ namespace FreeSql
{ {
var tbref = tr.Value; var tbref = tr.Value;
if (tbref.Exception != null) continue; 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 navigateExpression = $"{navigatePath}{tr.Key}";
var depthTab = "".PadLeft(depth * 4); var depthTab = "".PadLeft(depth * 4);
var lambdaAlias = (char)((byte)'a' + (depth - 1)); var lambdaAlias = (char)((byte)'a' + (depth - 1));
@ -506,22 +573,33 @@ namespace FreeSql
case TableRefType.OneToOne: case TableRefType.OneToOne:
if (ignores.Any(a => a == tbref.RefEntityType)) break; if (ignores.Any(a => a == tbref.RefEntityType)) break;
code.Append("\r\n").Append(depthTab).Append(".Include(").Append(lambdaStr).Append(navigateExpression).Append(")"); code.Append("\r\n").Append(depthTab).Append(".Include(").Append(lambdaStr).Append(navigateExpression).Append(")");
code.Append(LocalGetAutoIncludeQueryStaicCode(depth, tbref.RefEntityType, navigateExpression, ignores)); if (boundaryAttr?.BreakThen != true)
code.Append(LocalGetAutoIncludeQueryStaicCode(depth, tbref.RefEntityType, navigateExpression, ignores));
break; break;
case TableRefType.OneToMany: case TableRefType.OneToMany:
code.Append("\r\n").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression); code.Append("\r\n").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression);
var thencode = LocalGetAutoIncludeQueryStaicCode(depth + 1, tbref.RefEntityType, "", new Stack<Type>(ignores.ToArray())); if (boundaryAttr?.BreakThen != true)
if (thencode.Length > 0) code.Append(", then => then").Append(thencode); {
var thencode = LocalGetAutoIncludeQueryStaicCode(depth + 1, tbref.RefEntityType, "", new Stack<Type>(ignores.ToArray()));
if (thencode.Length > 0) code.Append(", then => then").Append(thencode);
}
code.Append(")"); code.Append(")");
break; break;
case TableRefType.ManyToMany: case TableRefType.ManyToMany:
code.Append("\r\n").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression).Append(")"); code.Append("\r\n").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression);
if (boundaryAttr?.BreakThen == false)
{
var thencode2 = LocalGetAutoIncludeQueryStaicCode(depth + 1, tbref.RefEntityType, "", new Stack<Type>(ignores.ToArray()));
if (thencode2.Length > 0) code.Append(", then => then").Append(thencode2);
}
code.Append(")");
break; break;
case TableRefType.PgArrayToMany: case TableRefType.PgArrayToMany:
code.Append("\r\n//").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression).Append(")");
break; break;
case TableRefType.ManyToOne: //不属于聚合根 case TableRefType.ManyToOne:
code.Append("\r\n//").Append(depthTab).Append(".Include(").Append(lambdaStr).Append(navigateExpression).Append(")"); 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; break;
} }
} }
@ -557,6 +635,7 @@ namespace FreeSql
} }
return middles; return middles;
} }
public static void SetNavigateRelationshipValue(IFreeSql orm, TableRef tbref, Type leftType, object leftItem, object rightItem) public static void SetNavigateRelationshipValue(IFreeSql orm, TableRef tbref, Type leftType, object leftItem, object rightItem)
{ {
if (rightItem == null) return; if (rightItem == null) return;

View File

@ -26,7 +26,7 @@ namespace FreeSql.Tests.DbContext2
{ {
new UserRepository(fsql, null); new UserRepository(fsql, null);
var code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(fsql, typeof(User)); var code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(null, fsql, typeof(User));
Assert.Equal(@"//fsql.Select<User>() Assert.Equal(@"//fsql.Select<User>()
SelectDiy SelectDiy
.Include(a => a.Ext)", code); .Include(a => a.Ext)", code);
@ -147,7 +147,6 @@ SelectDiy
public int Id { get; set; } public int Id { get; set; }
public string UserName { get; set; } public string UserName { get; set; }
public string Password { get; set; } public string Password { get; set; }
[Navigate(nameof(Id))]
public UserExt Ext { get; set; } public UserExt Ext { get; set; }
} }
class UserExt class UserExt
@ -155,7 +154,6 @@ SelectDiy
[Column(IsPrimary = true)] [Column(IsPrimary = true)]
public int UserId { get; set; } public int UserId { get; set; }
public string Remark { get; set; } public string Remark { get; set; }
[Navigate(nameof(UserId))]
public User Org { get; set; } public User Org { get; set; }
} }
} }

View File

@ -1,4 +1,5 @@
using FreeSql.DataAnnotations; using FreeSql.DataAnnotations;
using FreeSql.Internal;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
@ -27,7 +28,16 @@ namespace FreeSql.Tests.DbContext2
{ {
new OrderRepository(fsql, null); new OrderRepository(fsql, null);
var code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(fsql, typeof(Order));
fsql.Aop.CommandAfter += (_, e) =>
{
if (e.Exception is DbUpdateVersionException)
{
throw new Exception(e.Exception.Message, e.Exception);
}
};
var code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(null, fsql, typeof(Order));
Assert.Equal(@"//fsql.Select<Order>() Assert.Equal(@"//fsql.Select<Order>()
SelectDiy SelectDiy
.Include(a => a.Extdata) .Include(a => a.Extdata)
@ -165,7 +175,6 @@ SelectDiy
public int Id { get; set; } public int Id { get; set; }
public string Field2 { get; set; } public string Field2 { get; set; }
[Navigate(nameof(Id))]
public OrderExt Extdata { get; set; } public OrderExt Extdata { get; set; }
[Navigate(nameof(OrderDetail.OrderId))] [Navigate(nameof(OrderDetail.OrderId))]
public List<OrderDetail> Details { get; set; } public List<OrderDetail> Details { get; set; }
@ -178,7 +187,6 @@ SelectDiy
public int OrderId { get; set; } public int OrderId { get; set; }
public string Field3 { get; set; } public string Field3 { get; set; }
[Navigate(nameof(OrderId))]
public Order Order { get; set; } public Order Order { get; set; }
} }
class OrderDetail class OrderDetail
@ -188,7 +196,7 @@ SelectDiy
public int OrderId { get; set; } public int OrderId { get; set; }
public string Field4 { get; set; } public string Field4 { get; set; }
[Navigate(nameof(Id))] [AggregateRootBoundary("name1", Break = true)]
public OrderDetailExt Extdata { get; set; } public OrderDetailExt Extdata { get; set; }
} }
class OrderDetailExt class OrderDetailExt
@ -197,7 +205,6 @@ SelectDiy
public int OrderDetailId { get; set; } public int OrderDetailId { get; set; }
public string Field5 { get; set; } public string Field5 { get; set; }
[Navigate(nameof(OrderDetailId))]
public OrderDetail OrderDetail { get; set; } public OrderDetail OrderDetail { get; set; }
} }
class OrderTag class OrderTag