AggregateRootRepository Boundary

This commit is contained in:
2881099 2022-09-05 13:08:22 +08:00
parent 9db121d531
commit f601d9b9e0
7 changed files with 174 additions and 118 deletions

View File

@ -4,6 +4,12 @@ using System.Linq;
namespace FreeSql.DataAnnotations namespace FreeSql.DataAnnotations
{ {
/// <summary>
/// 设置 AggregateRootRepository 边界范围<para></para>
/// 在边界范围之内的规则 <para></para>
/// 1、OneToOne/OneToMany/ManyToMany(中间表) 可以查询、可以增删改<para></para>
/// 2、ManyToOne/ManyToMany外部表/PgArrayToMany 只可以查询,不支持增删改(会被忽略)<para></para>
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)] [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
public class AggregateRootBoundaryAttribute : Attribute public class AggregateRootBoundaryAttribute : Attribute
{ {

View File

@ -3,10 +3,15 @@ using System.Collections.Generic;
namespace FreeSql.Internal.Model namespace FreeSql.Internal.Model
{ {
public class AggregateRootTrackingChangeInfo public class AggregateRootTrackingChangeInfo
{ {
public List<NativeTuple<Type, object>> InsertLog { get; } = new List<NativeTuple<Type, object>>(); public List<NativeTuple<Type, object>> InsertLog { get; } = new List<NativeTuple<Type, object>>();
public List<NativeTuple<Type, object, object, List<string>>> UpdateLog { get; } = new List<NativeTuple<Type, object, object, List<string>>>(); public List<NativeTuple<Type, object, object, List<string>>> UpdateLog { get; } = new List<NativeTuple<Type, object, object, List<string>>>();
public List<NativeTuple<Type, object[]>> DeleteLog { get; } = new List<NativeTuple<Type, object[]>>(); public List<NativeTuple<Type, object[]>> DeleteLog { get; } = new List<NativeTuple<Type, object[]>>();
} }
} }

View File

@ -40,7 +40,7 @@ namespace FreeSql
DisposeChildRepositorys(); DisposeChildRepositorys();
_repository.FlushState(); _repository.FlushState();
FlushState(); FlushState();
_boundaryName = name; _boundaryName = string.Concat(name).Trim();
return this; return this;
} }

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(_boundaryName, _repository, GetChildRepository, entitys, out var affrows, cancellationToken); var ret = await InsertWithinBoundaryStaticAsync(_boundaryName, _repository, GetChildRepository, entitys, null, cancellationToken);
Attach(ret); Attach(ret);
return ret; return ret;
} }
@ -35,9 +35,116 @@ namespace FreeSql
_repository.FlushState(); _repository.FlushState();
} }
} }
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 async Task<List<T1>> InsertWithinBoundaryStaticAsync<T1>(string boundaryName, IBaseRepository<T1> rootRepository, Func<Type, IBaseRepository<object>> getChildRepository, IEnumerable<T1> rootEntitys, int[] affrows, CancellationToken cancellationToken) where T1 : class
{ {
return Task.FromResult(InsertWithinBoundaryStatic(boundaryName, rootRepository, getChildRepository, rootEntitys, out affrows)); Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
Dictionary<Type, IBaseRepository<object>> repos = new Dictionary<Type, IBaseRepository<object>>();
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<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, 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<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, boundaryAttr?.BreakThen != true);
}
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, false);
}
break;
case TableRefType.PgArrayToMany:
case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改
break;
}
}
return ret;
}
} }
#endregion #endregion
@ -138,8 +245,9 @@ namespace FreeSql
foreach (var il in insertLogDict) foreach (var il in insertLogDict)
{ {
var repo = GetChildRepository(il.Key); var repo = GetChildRepository(il.Key);
await InsertWithinBoundaryStaticAsync(_boundaryName, repo, GetChildRepository, il.Value, out var affrowsOut, cancellationToken); var affrowsOut = new int[1];
affrows += affrowsOut; await InsertWithinBoundaryStaticAsync(_boundaryName, repo, GetChildRepository, il.Value, affrowsOut, cancellationToken);
affrows += affrowsOut[0];
} }
for (var a = tracking.DeleteLog.Count - 1; a >= 0; a--) for (var a = tracking.DeleteLog.Count - 1; a >= 0; a--)

View File

@ -130,64 +130,6 @@ 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);
@ -250,7 +192,7 @@ namespace FreeSql
} }
break; break;
case TableRefType.PgArrayToMany: case TableRefType.PgArrayToMany:
case TableRefType.ManyToOne: //在插入前处理 case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改
break; break;
} }
} }

View File

@ -18,12 +18,17 @@ namespace FreeSql
{ {
public class AggregateRootUtils public class AggregateRootUtils
{ {
static ConcurrentDictionary<PropertyInfo, ConcurrentDictionary<string, AggregateRootBoundaryAttribute>> _dicGetPropertyBoundaryAttribute = new ConcurrentDictionary<PropertyInfo, ConcurrentDictionary<string, AggregateRootBoundaryAttribute>>();
public static AggregateRootBoundaryAttribute GetPropertyBoundaryAttribute(PropertyInfo prop, string boundaryName) public static AggregateRootBoundaryAttribute GetPropertyBoundaryAttribute(PropertyInfo prop, string boundaryName)
{ {
if (string.IsNullOrWhiteSpace(boundaryName)) return null; if (boundaryName == null) return null;
return _dicGetPropertyBoundaryAttribute.GetOrAdd(prop, tp => new ConcurrentDictionary<string, AggregateRootBoundaryAttribute>())
.GetOrAdd(boundaryName, bn =>
{
var attrs = prop.GetCustomAttributes(typeof(AggregateRootBoundaryAttribute), false); var attrs = prop.GetCustomAttributes(typeof(AggregateRootBoundaryAttribute), false);
if (attrs == null || attrs.Any() == false) return null; if (attrs == null || attrs.Any() == false) return null;
return attrs.Select(a => a as AggregateRootBoundaryAttribute).Where(a => a.Name == boundaryName).FirstOrDefault(); 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) public static void CompareEntityValue(string boundaryName, IFreeSql fsql, Type rootEntityType, object rootEntityBefore, object rootEntityAfter, string rootNavigatePropertyName, AggregateRootTrackingChangeInfo tracking)
@ -112,18 +117,9 @@ namespace FreeSql
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.RefMiddleEntityType, middleValuesBefore as IEnumerable, middleValuesAfter as IEnumerable, false); 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:
break; case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改
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;
} }
} }
@ -142,7 +138,7 @@ namespace FreeSql
//foreach (var item in collectionBefore as IEnumerable) //foreach (var item in collectionBefore as IEnumerable)
//{ //{
// changelog.DeleteLog.Add(NativeTuple.Create(elementType, new[] { item })); // changelog.DeleteLog.Add(NativeTuple.Create(elementType, new[] { item }));
// NavigateReader(fsql, elementType, item, (path, tr, ct, stackvs) => // NavigateReader(boundaryName, fsql, elementType, item, (path, tr, ct, stackvs) =>
// { // {
// var dellist = stackvs.Last() as object[] ?? new [] { stackvs.Last() }; // var dellist = stackvs.Last() as object[] ?? new [] { stackvs.Last() };
// changelog.DeleteLog.Add(NativeTuple.Create(ct, dellist)); // changelog.DeleteLog.Add(NativeTuple.Create(ct, dellist));
@ -338,35 +334,16 @@ namespace FreeSql
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:
break; case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改
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;
} }
} }
@ -423,18 +400,10 @@ namespace FreeSql
LocalMapEntityValueCollection(entityType, entityFrom, entityTo, tbref, propvalFrom as IEnumerable, prop, boundaryAttr?.BreakThen != 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, boundaryAttr?.BreakThen == false); LocalMapEntityValueCollection(entityType, entityFrom, entityTo, tbref, propvalFrom as IEnumerable, prop, false);
break; break;
case TableRefType.PgArrayToMany: case TableRefType.PgArrayToMany:
break; case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改
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;
} }
} }
@ -508,8 +477,10 @@ namespace FreeSql
LocalIncludeMany(tbref, navigateExp, boundaryAttr?.BreakThen == false); LocalIncludeMany(tbref, navigateExp, boundaryAttr?.BreakThen == false);
break; break;
case TableRefType.PgArrayToMany: case TableRefType.PgArrayToMany:
if (boundaryAttr?.Break == false)
LocalIncludeMany(tbref, navigateExp, boundaryAttr?.BreakThen == false);
break; break;
case TableRefType.ManyToOne: case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改
if (boundaryAttr?.Break == false) if (boundaryAttr?.Break == false)
{ {
LocalInclude(tbref, navigateExp); LocalInclude(tbref, navigateExp);
@ -589,14 +560,21 @@ namespace FreeSql
code.Append("\r\n").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression); code.Append("\r\n").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression);
if (boundaryAttr?.BreakThen == false) if (boundaryAttr?.BreakThen == false)
{ {
var thencode2 = LocalGetAutoIncludeQueryStaicCode(depth + 1, tbref.RefEntityType, "", new Stack<Type>(ignores.ToArray())); var thencode = LocalGetAutoIncludeQueryStaicCode(depth + 1, tbref.RefEntityType, "", new Stack<Type>(ignores.ToArray()));
if (thencode2.Length > 0) code.Append(", then => then").Append(thencode2); if (thencode.Length > 0) code.Append(", then => then").Append(thencode);
} }
code.Append(")"); code.Append(")");
break; break;
case TableRefType.PgArrayToMany: 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<Type>(ignores.ToArray()));
if (thencode.Length > 0) code.Append(", then => then").Append(thencode);
}
code.Append(")");
break; break;
case TableRefType.ManyToOne: case TableRefType.ManyToOne: //ManyToOne、ManyToMany外部表、PgArrayToMany 不属于聚合根成员,可以查询,不能增删改
code.Append("\r\n").Append(boundaryAttr != null ? "" : "//").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) if (boundaryAttr?.BreakThen == false)
code.Append(LocalGetAutoIncludeQueryStaicCode(depth, tbref.RefEntityType, navigateExpression, ignores)); code.Append(LocalGetAutoIncludeQueryStaicCode(depth, tbref.RefEntityType, navigateExpression, ignores));
@ -638,10 +616,10 @@ namespace FreeSql
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;
switch (tbref.RefType) switch (tbref.RefType)
{ {
case TableRefType.OneToOne: case TableRefType.OneToOne:
if (rightItem == null) return;
for (var idx = 0; idx < tbref.Columns.Count; idx++) 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)); var colval = Utils.GetDataReaderValue(tbref.RefColumns[idx].CsType, EntityUtilExtensions.GetEntityValueWithPropertyName(orm, leftType, leftItem, tbref.Columns[idx].CsName));
@ -649,6 +627,7 @@ namespace FreeSql
} }
break; break;
case TableRefType.OneToMany: case TableRefType.OneToMany:
if (rightItem == null) return;
var rightEachOtm = rightItem as IEnumerable; var rightEachOtm = rightItem as IEnumerable;
if (rightEachOtm == null) break; if (rightEachOtm == null) break;
var leftColValsOtm = new object[tbref.Columns.Count]; var leftColValsOtm = new object[tbref.Columns.Count];
@ -661,7 +640,9 @@ namespace FreeSql
case TableRefType.ManyToOne: case TableRefType.ManyToOne:
for (var idx = 0; idx < tbref.RefColumns.Count; idx++) for (var idx = 0; idx < tbref.RefColumns.Count; idx++)
{ {
var colval = Utils.GetDataReaderValue(tbref.Columns[idx].CsType, EntityUtilExtensions.GetEntityValueWithPropertyName(orm, tbref.RefEntityType, rightItem, tbref.RefColumns[idx].CsName)); 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); EntityUtilExtensions.SetEntityValueWithPropertyName(orm, leftType, leftItem, tbref.Columns[idx].CsName, colval);
} }
break; break;

View File

@ -38,12 +38,25 @@ namespace FreeSql.Tests.DbContext2
}; };
var code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(null, fsql, typeof(Order)); var code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(null, fsql, typeof(Order));
var code1 = AggregateRootUtils.GetAutoIncludeQueryStaicCode("code1", fsql, typeof(Order));
var code2 = AggregateRootUtils.GetAutoIncludeQueryStaicCode("code2", fsql, typeof(Order));
Assert.Equal(@"//fsql.Select<Order>() Assert.Equal(@"//fsql.Select<Order>()
SelectDiy SelectDiy
.Include(a => a.Extdata) .Include(a => a.Extdata)
.IncludeMany(a => a.Details, then => then .IncludeMany(a => a.Details, then => then
.Include(b => b.Extdata)) .Include(b => b.Extdata))
.IncludeMany(a => a.Tags)", code); .IncludeMany(a => a.Tags)", code);
Assert.Equal(@"//fsql.Select<Order>()
SelectDiy
.Include(a => a.Extdata)
.IncludeMany(a => a.Details, then => then
.Include(b => b.Extdata))
.IncludeMany(a => a.Tags, then => then
.IncludeMany(b => b.Orders))", code1);
Assert.Equal(@"//fsql.Select<Order>()
SelectDiy
.IncludeMany(a => a.Details)
.IncludeMany(a => a.Tags)", code2);
fsql.Insert(new[] fsql.Insert(new[]
{ {
@ -175,10 +188,11 @@ SelectDiy
public int Id { get; set; } public int Id { get; set; }
public string Field2 { get; set; } public string Field2 { get; set; }
[AggregateRootBoundary("code2", Break = true)]
public OrderExt Extdata { get; set; } public OrderExt Extdata { get; set; }
[Navigate(nameof(OrderDetail.OrderId))] [Navigate(nameof(OrderDetail.OrderId)), AggregateRootBoundary("code2", BreakThen = true)]
public List<OrderDetail> Details { get; set; } public List<OrderDetail> Details { get; set; }
[Navigate(ManyToMany = typeof(OrderTag))] [Navigate(ManyToMany = typeof(OrderTag)), AggregateRootBoundary("code1", Break = false, BreakThen = false)]
public List<Tag> Tags { get; set; } public List<Tag> Tags { get; set; }
} }
class OrderExt class OrderExt