mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-04-22 02:32:50 +08:00
AggregateRootRepository
This commit is contained in:
parent
319467dcc4
commit
a590b8aa7b
24
FreeSql.Repository/AggregateRootModel.cs
Normal file
24
FreeSql.Repository/AggregateRootModel.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using FreeSql;
|
||||||
|
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.Internal.Model
|
||||||
|
{
|
||||||
|
public class AggregateRootTrackingChangeInfo
|
||||||
|
{
|
||||||
|
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[]>> DeleteLog { get; } = new List<NativeTuple<Type, object[]>>();
|
||||||
|
}
|
||||||
|
}
|
@ -17,18 +17,22 @@ namespace FreeSql
|
|||||||
{
|
{
|
||||||
partial class AggregateRootRepository<TEntity>
|
partial class AggregateRootRepository<TEntity>
|
||||||
{
|
{
|
||||||
public Task<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default) => _repository.InsertAsync(entity, cancellationToken);
|
//以下临时调用同步方法
|
||||||
public Task<List<TEntity>> InsertAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => _repository.InsertAsync(entitys, cancellationToken);
|
public Task<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default) => Task.FromResult(Insert(entity));
|
||||||
public Task<TEntity> InsertOrUpdateAsync(TEntity entity, CancellationToken cancellationToken = default) => _repository.InsertOrUpdateAsync(entity, cancellationToken);
|
public Task<List<TEntity>> InsertAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => Task.FromResult(Insert(entitys));
|
||||||
public Task SaveManyAsync(TEntity entity, string propertyName, CancellationToken cancellationToken = default) => _repository.SaveManyAsync(entity, propertyName, cancellationToken);
|
public Task<TEntity> InsertOrUpdateAsync(TEntity entity, CancellationToken cancellationToken = default) => Task.FromResult(InsertOrUpdate(entity));
|
||||||
|
async public Task SaveManyAsync(TEntity entity, string propertyName, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
SaveMany(entity, propertyName);
|
||||||
|
}
|
||||||
|
|
||||||
public Task<int> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default) => _repository.UpdateAsync(entity, cancellationToken);
|
public Task<int> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default) => Task.FromResult(Update(entity));
|
||||||
public Task<int> UpdateAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => _repository.UpdateAsync(entitys, cancellationToken);
|
public Task<int> UpdateAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => Task.FromResult(Update(entitys));
|
||||||
|
|
||||||
public Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default) => _repository.DeleteAsync(entity, cancellationToken);
|
public Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default) => Task.FromResult(Delete(entity));
|
||||||
public Task<int> DeleteAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => _repository.DeleteAsync(entitys, cancellationToken);
|
public Task<int> DeleteAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => Task.FromResult(Delete(entitys));
|
||||||
public Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => _repository.DeleteAsync(predicate, cancellationToken);
|
public Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => Task.FromResult(Delete(predicate));
|
||||||
public Task<List<object>> DeleteCascadeByDatabaseAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => _repository.DeleteCascadeByDatabaseAsync(predicate, cancellationToken);
|
public Task<List<object>> DeleteCascadeByDatabaseAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => Task.FromResult(DeleteCascadeByDatabase(predicate));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ namespace FreeSql
|
|||||||
localAffrows += ret.Count;
|
localAffrows += ret.Count;
|
||||||
foreach (var entity in entitys) LocalCanAggregateRoot(repository.EntityType, entity, true);
|
foreach (var entity in entitys) LocalCanAggregateRoot(repository.EntityType, entity, true);
|
||||||
|
|
||||||
foreach (var tr in table.GetAllTableRef())
|
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;
|
||||||
@ -180,36 +180,35 @@ namespace FreeSql
|
|||||||
}
|
}
|
||||||
protected virtual int UpdateAggregateRoot(IEnumerable<TEntity> entitys)
|
protected virtual int UpdateAggregateRoot(IEnumerable<TEntity> entitys)
|
||||||
{
|
{
|
||||||
List<NativeTuple<Type, object>> insertLog = new List<NativeTuple<Type, object>>();
|
var tracking = new AggregateRootTrackingChangeInfo();
|
||||||
List<NativeTuple<Type, object, object, List<string>>> updateLog = new List<NativeTuple<Type, object, object, List<string>>>();
|
|
||||||
List<NativeTuple<Type, object[]>> deleteLog = new List<NativeTuple<Type, object[]>>();
|
|
||||||
foreach(var entity in entitys)
|
foreach(var entity in entitys)
|
||||||
{
|
{
|
||||||
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, insertLog, updateLog, deleteLog);
|
AggregateRootUtils.CompareEntityValue(Orm, EntityType, state.Value, entity, null, tracking);
|
||||||
}
|
}
|
||||||
var affrows = 0;
|
var affrows = 0;
|
||||||
|
|
||||||
DisposeChildRepositorys();
|
DisposeChildRepositorys();
|
||||||
var insertLogDict = insertLog.GroupBy(a => a.Item1).ToDictionary(a => a.Key, a => insertLog.Where(b => b.Item1 == a.Key).Select(b => b.Item2).ToArray());
|
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)
|
foreach (var il in insertLogDict)
|
||||||
{
|
{
|
||||||
InsertAggregateRootStatic(GetChildRepository(il.Key), GetChildRepository, il.Value, out var affrowsOut);
|
var repo = GetChildRepository(il.Key);
|
||||||
|
InsertAggregateRootStatic(repo, GetChildRepository, il.Value, out var affrowsOut);
|
||||||
affrows += affrowsOut;
|
affrows += affrowsOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var a = 0; a < deleteLog.Count - 1; a++)
|
for (var a = tracking.DeleteLog.Count - 1; a >= 0; a--)
|
||||||
affrows += Orm.Delete<object>().AsType(deleteLog[a].Item1).WhereDynamic(deleteLog[a].Item2).ExecuteAffrows();
|
affrows += Orm.Delete<object>().AsType(tracking.DeleteLog[a].Item1).AsTable(_asTableRule)
|
||||||
|
.WhereDynamic(tracking.DeleteLog[a].Item2).ExecuteAffrows();
|
||||||
|
|
||||||
var updateLogDict = updateLog.GroupBy(a => a.Item1).ToDictionary(a => a.Key, a => updateLog.Where(b => b.Item1 == a.Key).Select(b =>
|
var updateLogDict = tracking.UpdateLog.GroupBy(a => a.Item1).ToDictionary(a => a.Key, a => tracking.UpdateLog.Where(b => b.Item1 == a.Key).Select(b =>
|
||||||
NativeTuple.Create(b.Item2, b.Item3, string.Join(",", b.Item4.OrderBy(c => c)), b.Item4)).ToArray());
|
NativeTuple.Create(b.Item2, b.Item3, string.Join(",", b.Item4.OrderBy(c => c)), b.Item4)).ToArray());
|
||||||
var updateLogDict2 = updateLogDict.ToDictionary(a => a.Key, a => a.Value.ToDictionary(b => b.Item3, b => a.Value.Where(c => c.Item3 == b.Item3).ToArray()));
|
var updateLogDict2 = updateLogDict.ToDictionary(a => a.Key, a => a.Value.ToDictionary(b => b.Item3, b => a.Value.Where(c => c.Item3 == b.Item3).ToArray()));
|
||||||
foreach (var dl in updateLogDict2)
|
foreach (var dl in updateLogDict2)
|
||||||
{
|
{
|
||||||
foreach (var dl2 in dl.Value)
|
foreach (var dl2 in dl.Value)
|
||||||
{
|
{
|
||||||
affrows += Orm.Update<object>().AsType(dl.Key)
|
affrows += Orm.Update<object>().AsType(dl.Key).AsTable(_asTableRule)
|
||||||
.SetSource(dl2.Value.Select(a => a.Item2).ToArray())
|
.SetSource(dl2.Value.Select(a => a.Item2).ToArray())
|
||||||
.UpdateColumns(dl2.Value.First().Item4.ToArray())
|
.UpdateColumns(dl2.Value.First().Item4.ToArray())
|
||||||
.ExecuteAffrows();
|
.ExecuteAffrows();
|
||||||
@ -223,27 +222,29 @@ namespace FreeSql
|
|||||||
}
|
}
|
||||||
protected virtual int DeleteAggregateRoot(IEnumerable<TEntity> entitys, List<object> deletedOutput = null)
|
protected virtual int DeleteAggregateRoot(IEnumerable<TEntity> entitys, List<object> deletedOutput = null)
|
||||||
{
|
{
|
||||||
List<NativeTuple<Type, object>> insertLog = new List<NativeTuple<Type, object>>();
|
var tracking = new AggregateRootTrackingChangeInfo();
|
||||||
List<NativeTuple<Type, object, object, List<string>>> updateLog = new List<NativeTuple<Type, object, object, List<string>>>();
|
|
||||||
List<NativeTuple<Type, object[]>> deleteLog = new List<NativeTuple<Type, object[]>>();
|
|
||||||
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, insertLog, updateLog, deleteLog);
|
AggregateRootUtils.CompareEntityValue(Orm, EntityType, entity, null, null, tracking);
|
||||||
_states.Remove(stateKey);
|
_states.Remove(stateKey);
|
||||||
}
|
}
|
||||||
if (deletedOutput != null) deletedOutput.AddRange(deleteLog.Select(a => a.Item2));
|
var affrows = 0;
|
||||||
return deleteLog.Count;
|
for (var a = tracking.DeleteLog.Count - 1; a >= 0; a--)
|
||||||
|
{
|
||||||
|
affrows += Orm.Delete<object>().AsType(tracking.DeleteLog[a].Item1).AsTable(_asTableRule)
|
||||||
|
.WhereDynamic(tracking.DeleteLog[a].Item2).ExecuteAffrows();
|
||||||
|
if (deletedOutput != null) deletedOutput.AddRange(tracking.DeleteLog[a].Item2);
|
||||||
|
}
|
||||||
|
return affrows;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void SaveManyAggregateRoot(TEntity entity, string propertyName)
|
protected virtual void SaveManyAggregateRoot(TEntity entity, string propertyName)
|
||||||
{
|
{
|
||||||
List<NativeTuple<Type, object>> insertLog = new List<NativeTuple<Type, object>>();
|
var tracking = new AggregateRootTrackingChangeInfo();
|
||||||
List<NativeTuple<Type, object, object, List<string>>> updateLog = new List<NativeTuple<Type, object, object, List<string>>>();
|
|
||||||
List<NativeTuple<Type, object[]>> deleteLog = new List<NativeTuple<Type, object[]>>();
|
|
||||||
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, insertLog, updateLog, deleteLog);
|
AggregateRootUtils.CompareEntityValue(Orm, EntityType, state.Value, entity, propertyName, tracking);
|
||||||
Attach(entity);
|
Attach(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,9 +274,7 @@ namespace FreeSql
|
|||||||
{
|
{
|
||||||
if (data == null) data = _dataEditing;
|
if (data == null) data = _dataEditing;
|
||||||
if (data == null) return 0;
|
if (data == null) return 0;
|
||||||
List<NativeTuple<Type, object>> insertLog = new List<NativeTuple<Type, object>>();
|
var tracking = new AggregateRootTrackingChangeInfo();
|
||||||
List<NativeTuple<Type, object, object, List<string>>> updateLog = new List<NativeTuple<Type, object, object, List<string>>>();
|
|
||||||
List<NativeTuple<Type, object[]>> deleteLog = new List<NativeTuple<Type, object[]>>();
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var addList = new List<TEntity>();
|
var addList = new List<TEntity>();
|
||||||
@ -285,23 +284,23 @@ namespace FreeSql
|
|||||||
var key = Orm.GetEntityKeyString(EntityType, item, false);
|
var key = Orm.GetEntityKeyString(EntityType, item, false);
|
||||||
if (_statesEditing.TryRemove(key, out var state) == false)
|
if (_statesEditing.TryRemove(key, out var state) == false)
|
||||||
{
|
{
|
||||||
insertLog.Add(NativeTuple.Create(EntityType, (object)item));
|
tracking.InsertLog.Add(NativeTuple.Create(EntityType, (object)item));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
_states[key] = state;
|
_states[key] = state;
|
||||||
AggregateRootUtils.CompareEntityValue(Orm, EntityType, state.Value, item, null, insertLog, updateLog, deleteLog);
|
AggregateRootUtils.CompareEntityValue(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(Orm, EntityType, item, null, null, insertLog, updateLog, deleteLog);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
_dataEditing = null;
|
_dataEditing = null;
|
||||||
_statesEditing.Clear();
|
_statesEditing.Clear();
|
||||||
}
|
}
|
||||||
return insertLog.Count + updateLog.Count + deleteLog.Count;
|
return tracking.InsertLog.Count + tracking.UpdateLog.Count + tracking.DeleteLog.Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,488 +13,491 @@ using System.Linq.Expressions;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
static class AggregateRootUtils
|
namespace FreeSql
|
||||||
{
|
{
|
||||||
public static void CompareEntityValue(IFreeSql fsql, Type rootEntityType, object rootEntityBefore, object rootEntityAfter, string rootNavigatePropertyName,
|
public static class AggregateRootUtils
|
||||||
List<NativeTuple<Type, object>> insertLog,
|
|
||||||
List<NativeTuple<Type, object, object, List<string>>> updateLog,
|
|
||||||
List<NativeTuple<Type, object[]>> deleteLog)
|
|
||||||
{
|
{
|
||||||
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
|
public static void CompareEntityValue(IFreeSql fsql, Type rootEntityType, object rootEntityBefore, object rootEntityAfter, string rootNavigatePropertyName, AggregateRootTrackingChangeInfo tracking)
|
||||||
LocalCompareEntityValue(rootEntityType, rootEntityBefore, rootEntityAfter, rootNavigatePropertyName);
|
|
||||||
ignores.Clear();
|
|
||||||
|
|
||||||
void LocalCompareEntityValue(Type entityType, object entityBefore, object entityAfter, string navigatePropertyName)
|
|
||||||
{
|
{
|
||||||
if (entityType == null) entityType = entityBefore?.GetType() ?? entityAfter?.GetType();
|
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
|
||||||
|
LocalCompareEntityValue(rootEntityType, rootEntityBefore, rootEntityAfter, rootNavigatePropertyName);
|
||||||
|
ignores.Clear();
|
||||||
|
|
||||||
if (entityBefore != null)
|
void LocalCompareEntityValue(Type entityType, object entityBefore, object entityAfter, string navigatePropertyName)
|
||||||
{
|
{
|
||||||
var stateKey = $":before://{fsql.GetEntityKeyString(entityType, entityBefore, false)}";
|
if (entityType == null) entityType = entityBefore?.GetType() ?? entityAfter?.GetType();
|
||||||
if (ignores.TryGetValue(entityType, out var stateKeys) == false) ignores.Add(entityType, stateKeys = new Dictionary<string, bool>());
|
|
||||||
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<string, bool>());
|
|
||||||
if (stateKeys.ContainsKey(stateKey)) return;
|
|
||||||
stateKeys.Add(stateKey, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
var table = fsql.CodeFirst.GetTableByEntity(entityType);
|
if (entityBefore != null)
|
||||||
if (table == null) return;
|
|
||||||
if (entityBefore == null && entityAfter == null) return;
|
|
||||||
if (entityBefore == null && entityAfter != null)
|
|
||||||
{
|
|
||||||
insertLog.Add(NativeTuple.Create(entityType, entityAfter));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (entityBefore != null && entityAfter == null)
|
|
||||||
{
|
|
||||||
deleteLog.Add(NativeTuple.Create(entityType, new[] { entityBefore }));
|
|
||||||
NavigateReader(fsql, entityType, entityBefore, (path, tr, ct, stackvs) =>
|
|
||||||
{
|
{
|
||||||
var dellist = stackvs.First() as object[] ?? new[] { stackvs.First() };
|
var stateKey = $":before://{fsql.GetEntityKeyString(entityType, entityBefore, false)}";
|
||||||
deleteLog.Add(NativeTuple.Create(ct, dellist));
|
if (ignores.TryGetValue(entityType, out var stateKeys) == false) ignores.Add(entityType, stateKeys = new Dictionary<string, bool>());
|
||||||
});
|
if (stateKeys.ContainsKey(stateKey)) return;
|
||||||
return;
|
stateKeys.Add(stateKey, true);
|
||||||
}
|
|
||||||
var changes = new List<string>();
|
|
||||||
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);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
if (entityAfter != null)
|
||||||
if (changes.Any())
|
|
||||||
updateLog.Add(NativeTuple.Create(entityType, entityBefore, entityAfter, changes));
|
|
||||||
|
|
||||||
foreach (var tr in table.GetAllTableRef())
|
|
||||||
{
|
|
||||||
var tbref = tr.Value;
|
|
||||||
if (tbref.Exception != null) continue;
|
|
||||||
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) 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:
|
var stateKey = $":after://{fsql.GetEntityKeyString(entityType, entityAfter, false)}";
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityBefore, propvalBefore);
|
if (ignores.TryGetValue(entityType, out var stateKeys) == false) ignores.Add(entityType, stateKeys = new Dictionary<string, bool>());
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityAfter, propvalAfter);
|
if (stateKeys.ContainsKey(stateKey)) return;
|
||||||
LocalCompareEntityValue(tbref.RefEntityType, propvalBefore, propvalAfter, null);
|
stateKeys.Add(stateKey, true);
|
||||||
break;
|
|
||||||
case TableRefType.OneToMany:
|
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityBefore, propvalBefore);
|
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityAfter, propvalAfter);
|
|
||||||
LocalCompareEntityValueCollection(tbref, propvalBefore as IEnumerable, propvalAfter as IEnumerable);
|
|
||||||
break;
|
|
||||||
case TableRefType.ManyToMany:
|
|
||||||
var middleValuesBefore = GetManyToManyObjects(fsql, table, tbref, entityBefore, prop);
|
|
||||||
var middleValuesAfter = GetManyToManyObjects(fsql, table, tbref, entityAfter, prop);
|
|
||||||
LocalCompareEntityValueCollection(tbref, middleValuesBefore as IEnumerable, middleValuesAfter as IEnumerable);
|
|
||||||
break;
|
|
||||||
case TableRefType.PgArrayToMany:
|
|
||||||
case TableRefType.ManyToOne: //不属于聚合根
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
var table = fsql.CodeFirst.GetTableByEntity(entityType);
|
||||||
void LocalCompareEntityValueCollection(TableRef tbref, IEnumerable collectionBefore, IEnumerable collectionAfter)
|
if (table == null) return;
|
||||||
{
|
if (entityBefore == null && entityAfter == null) return;
|
||||||
var elementType = tbref.RefType == TableRefType.ManyToMany ? tbref.RefMiddleEntityType : tbref.RefEntityType;
|
if (entityBefore == null && entityAfter != null)
|
||||||
if (collectionBefore == null && collectionAfter == null) return;
|
|
||||||
if (collectionBefore == null && collectionAfter != null)
|
|
||||||
{
|
|
||||||
foreach (var item in collectionAfter)
|
|
||||||
insertLog.Add(NativeTuple.Create(elementType, item));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (collectionBefore != null && collectionAfter == null)
|
|
||||||
{
|
|
||||||
//foreach (var item in collectionBefore as IEnumerable)
|
|
||||||
//{
|
|
||||||
// deleteLog.Add(NativeTuple.Create(elementType, new[] { item }));
|
|
||||||
// NavigateReader(fsql, elementType, item, (path, tr, ct, stackvs) =>
|
|
||||||
// {
|
|
||||||
// var dellist = stackvs.First() as object[] ?? new [] { stackvs.First() };
|
|
||||||
// deleteLog.Add(NativeTuple.Create(ct, dellist));
|
|
||||||
// });
|
|
||||||
//}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Dictionary<string, object> dictBefore = new Dictionary<string, object>();
|
|
||||||
Dictionary<string, object> dictAfter = new Dictionary<string, object>();
|
|
||||||
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) insertLog.Add(NativeTuple.Create(elementType, item));
|
|
||||||
else dictAfter.Add(key, item);
|
|
||||||
}
|
|
||||||
foreach (var key in dictBefore.Keys.ToArray())
|
|
||||||
{
|
|
||||||
if (dictAfter.ContainsKey(key) == false)
|
|
||||||
{
|
{
|
||||||
var value = dictBefore[key];
|
tracking.InsertLog.Add(NativeTuple.Create(entityType, entityAfter));
|
||||||
deleteLog.Add(NativeTuple.Create(elementType, new[] { value }));
|
|
||||||
NavigateReader(fsql, elementType, value, (path, tr, ct, stackvs) =>
|
|
||||||
{
|
|
||||||
var dellist = stackvs.First() as object[] ?? new[] { stackvs.First() };
|
|
||||||
deleteLog.Add(NativeTuple.Create(ct, dellist));
|
|
||||||
});
|
|
||||||
dictBefore.Remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (var key in dictAfter.Keys.ToArray())
|
|
||||||
{
|
|
||||||
if (dictBefore.ContainsKey(key) == false)
|
|
||||||
{
|
|
||||||
insertLog.Add(NativeTuple.Create(elementType, dictAfter[key]));
|
|
||||||
dictAfter.Remove(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach (var key in dictBefore.Keys)
|
|
||||||
LocalCompareEntityValue(elementType, dictBefore[key], dictAfter[key], null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void NavigateReader(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>>();
|
|
||||||
var statckPath = new Stack<string>();
|
|
||||||
var stackValues = new List<object>();
|
|
||||||
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<string, bool>());
|
|
||||||
if (stateKeys.ContainsKey(stateKey)) return;
|
|
||||||
stateKeys.Add(stateKey, true);
|
|
||||||
|
|
||||||
foreach (var tr in table.GetAllTableRef())
|
|
||||||
{
|
|
||||||
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 propval = table.GetPropertyValue(entity, prop.Name);
|
|
||||||
statckPath.Push(prop.Name);
|
|
||||||
stackValues.Add(propval);
|
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entity, propval);
|
|
||||||
callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefEntityType, stackValues);
|
|
||||||
LocalNavigateReader(tbref.RefEntityType, propval);
|
|
||||||
stackValues.RemoveAt(stackValues.Count - 1);
|
|
||||||
statckPath.Pop();
|
|
||||||
break;
|
|
||||||
case TableRefType.OneToMany:
|
|
||||||
var propvalOtm = table.GetPropertyValue(entity, prop.Name);
|
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entity, propvalOtm);
|
|
||||||
var propvalOtmList = new List<object>();
|
|
||||||
foreach (var val in propvalOtm as IEnumerable)
|
|
||||||
propvalOtmList.Add(val);
|
|
||||||
statckPath.Push($"{prop.Name}[]");
|
|
||||||
stackValues.Add(propvalOtmList.ToArray());
|
|
||||||
callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefEntityType, stackValues);
|
|
||||||
foreach (var val in propvalOtm as IEnumerable)
|
|
||||||
LocalNavigateReader(tbref.RefEntityType, val);
|
|
||||||
stackValues.RemoveAt(stackValues.Count - 1);
|
|
||||||
statckPath.Pop();
|
|
||||||
break;
|
|
||||||
case TableRefType.ManyToMany:
|
|
||||||
var middleValues = GetManyToManyObjects(fsql, table, tbref, entity, prop).ToArray();
|
|
||||||
statckPath.Push($"{prop.Name}[]");
|
|
||||||
stackValues.Add(middleValues);
|
|
||||||
callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefEntityType, stackValues);
|
|
||||||
stackValues.RemoveAt(stackValues.Count - 1);
|
|
||||||
statckPath.Pop();
|
|
||||||
break;
|
|
||||||
case TableRefType.PgArrayToMany:
|
|
||||||
case TableRefType.ManyToOne: //不属于聚合根
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void MapEntityValue(IFreeSql fsql, Type rootEntityType, object rootEntityFrom, object rootEntityTo)
|
|
||||||
{
|
|
||||||
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
|
|
||||||
LocalMapEntityValue(rootEntityType, rootEntityFrom, rootEntityTo);
|
|
||||||
ignores.Clear();
|
|
||||||
|
|
||||||
void LocalMapEntityValue(Type entityType, object entityFrom, object entityTo)
|
|
||||||
{
|
|
||||||
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<string, bool>());
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
var tbref = table.GetTableRef(prop.Name, false);
|
|
||||||
if (tbref == null) continue;
|
|
||||||
var propvalFrom = EntityUtilExtensions.GetEntityValueWithPropertyName(fsql, entityType, entityFrom, prop.Name);
|
|
||||||
if (propvalFrom == null)
|
|
||||||
{
|
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, null);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (tbref.RefType)
|
if (entityBefore != null && entityAfter == null)
|
||||||
{
|
{
|
||||||
case TableRefType.OneToOne:
|
tracking.DeleteLog.Add(NativeTuple.Create(entityType, new[] { entityBefore }));
|
||||||
var propvalTo = tbref.RefEntityType.CreateInstanceGetDefaultValue();
|
NavigateReader(fsql, entityType, entityBefore, (path, tr, ct, stackvs) =>
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityFrom, propvalFrom);
|
{
|
||||||
LocalMapEntityValue(tbref.RefEntityType, propvalFrom, propvalTo);
|
var dellist = stackvs.Last() as object[] ?? new[] { stackvs.Last() };
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, propvalTo);
|
tracking.DeleteLog.Add(NativeTuple.Create(ct, dellist));
|
||||||
break;
|
});
|
||||||
case TableRefType.OneToMany:
|
return;
|
||||||
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityFrom, propvalFrom);
|
|
||||||
LocalMapEntityValueCollection(entityType, entityFrom, entityTo, tbref, propvalFrom as IEnumerable, prop, true);
|
|
||||||
break;
|
|
||||||
case TableRefType.ManyToMany:
|
|
||||||
LocalMapEntityValueCollection(entityType, entityFrom, entityTo, tbref, propvalFrom as IEnumerable, prop, false);
|
|
||||||
break;
|
|
||||||
case TableRefType.PgArrayToMany:
|
|
||||||
case TableRefType.ManyToOne: //不属于聚合根
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
var changes = new List<string>();
|
||||||
}
|
foreach (var col in table.ColumnsByCs.Values)
|
||||||
void LocalMapEntityValueCollection(Type entityType, object entityFrom, object entityTo, TableRef tbref, IEnumerable propvalFrom, PropertyInfo prop, bool cascade)
|
{
|
||||||
{
|
if (table.ColumnsByCsIgnore.ContainsKey(col.CsName)) continue;
|
||||||
var propvalTo = typeof(List<>).MakeGenericType(tbref.RefEntityType).CreateInstanceGetDefaultValue();
|
if (table.ColumnsByCs.ContainsKey(col.CsName))
|
||||||
var propvalToIList = propvalTo as IList;
|
{
|
||||||
foreach (var fromItem in propvalFrom)
|
if (col.Attribute.IsVersion) continue;
|
||||||
{
|
var propvalBefore = table.GetPropertyValue(entityBefore, col.CsName);
|
||||||
var toItem = tbref.RefEntityType.CreateInstanceGetDefaultValue();
|
var propvalAfter = table.GetPropertyValue(entityAfter, col.CsName);
|
||||||
if (cascade) LocalMapEntityValue(tbref.RefEntityType, fromItem, toItem);
|
if (object.Equals(propvalBefore, propvalAfter) == false) changes.Add(col.CsName);
|
||||||
else EntityUtilExtensions.MapEntityValue(fsql, tbref.RefEntityType, fromItem, toItem);
|
continue;
|
||||||
propvalToIList.Add(toItem);
|
}
|
||||||
}
|
}
|
||||||
var propvalType = prop.PropertyType.GetGenericTypeDefinition();
|
if (changes.Any())
|
||||||
if (propvalType == typeof(List<>) || propvalType == typeof(ICollection<>))
|
tracking.UpdateLog.Add(NativeTuple.Create(entityType, entityBefore, entityAfter, changes));
|
||||||
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<Type, ConcurrentDictionary<Type, Action<ISelect0>>> _dicGetAutoIncludeQuery = new ConcurrentDictionary<Type, ConcurrentDictionary<Type, Action<ISelect0>>>();
|
foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key))
|
||||||
public static ISelect<TEntity> GetAutoIncludeQuery<TEntity>(ISelect<TEntity> select)
|
|
||||||
{
|
|
||||||
var select0p = select as Select0Provider;
|
|
||||||
var table0Type = select0p._tables[0].Table.Type;
|
|
||||||
var func = _dicGetAutoIncludeQuery.GetOrAdd(typeof(TEntity), t => new ConcurrentDictionary<Type, Action<ISelect0>>()).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<Type>());
|
|
||||||
return Expression.Lambda<Action<ISelect0>>(exp, parmExp1).Compile();
|
|
||||||
});
|
|
||||||
func(select);
|
|
||||||
return select;
|
|
||||||
Expression LocalGetAutoIncludeQuery(Expression queryExp, int depth, Type entityType, ParameterExpression navigateParameterExp, Expression navigatePathExp, Stack<Type> 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())
|
|
||||||
{
|
|
||||||
var tbref = tr.Value;
|
|
||||||
if (tbref.Exception != null) continue;
|
|
||||||
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue;
|
|
||||||
Expression navigateExp = Expression.MakeMemberAccess(navigatePathExp, prop);
|
|
||||||
//var lambdaAlias = (char)((byte)'a' + (depth - 1));
|
|
||||||
switch (tbref.RefType)
|
|
||||||
{
|
{
|
||||||
case TableRefType.OneToOne:
|
var tbref = tr.Value;
|
||||||
if (ignores.Any(a => a == tbref.RefEntityType)) break;
|
if (tbref.Exception != null) continue;
|
||||||
LocalInclude(tbref, navigateExp);
|
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue;
|
||||||
queryExp = LocalGetAutoIncludeQuery(queryExp, depth, tbref.RefEntityType, navigateParameterExp, navigateExp, ignores);
|
if (navigatePropertyName != null && prop.Name != navigatePropertyName) continue;
|
||||||
break;
|
var propvalBefore = table.GetPropertyValue(entityBefore, prop.Name);
|
||||||
case TableRefType.OneToMany:
|
var propvalAfter = table.GetPropertyValue(entityAfter, prop.Name);
|
||||||
LocalIncludeMany(tbref, navigateExp, true);
|
switch (tbref.RefType)
|
||||||
break;
|
{
|
||||||
case TableRefType.ManyToMany:
|
case TableRefType.OneToOne:
|
||||||
LocalIncludeMany(tbref, navigateExp, false);
|
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityBefore, propvalBefore);
|
||||||
break;
|
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityAfter, propvalAfter);
|
||||||
case TableRefType.PgArrayToMany:
|
LocalCompareEntityValue(tbref.RefEntityType, propvalBefore, propvalAfter, null);
|
||||||
break;
|
break;
|
||||||
case TableRefType.ManyToOne: //不属于聚合根
|
case TableRefType.OneToMany:
|
||||||
break;
|
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityBefore, propvalBefore);
|
||||||
|
SetNavigateRelationshipValue(fsql, tbref, table.Type, entityAfter, propvalAfter);
|
||||||
|
LocalCompareEntityValueCollection(tbref, propvalBefore as IEnumerable, propvalAfter as IEnumerable);
|
||||||
|
break;
|
||||||
|
case TableRefType.ManyToMany:
|
||||||
|
var middleValuesBefore = GetManyToManyObjects(fsql, table, tbref, entityBefore, prop);
|
||||||
|
var middleValuesAfter = GetManyToManyObjects(fsql, table, tbref, entityAfter, prop);
|
||||||
|
LocalCompareEntityValueCollection(tbref, middleValuesBefore as IEnumerable, middleValuesAfter as IEnumerable);
|
||||||
|
break;
|
||||||
|
case TableRefType.PgArrayToMany:
|
||||||
|
case TableRefType.ManyToOne: //不属于聚合根
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ignores.Pop();
|
void LocalCompareEntityValueCollection(TableRef tbref, IEnumerable collectionBefore, IEnumerable collectionAfter)
|
||||||
return queryExp;
|
|
||||||
void LocalInclude(TableRef tbref, Expression exp)
|
|
||||||
{
|
{
|
||||||
var incMethod = queryExp.Type.GetMethod("Include");
|
var elementType = tbref.RefType == TableRefType.ManyToMany ? tbref.RefMiddleEntityType : tbref.RefEntityType;
|
||||||
if (incMethod == null) throw new Exception(CoreStrings.RunTimeError_Reflection_IncludeMany.Replace("IncludeMany", "Include"));
|
if (collectionBefore == null && collectionAfter == null) return;
|
||||||
queryExp = Expression.Call(queryExp, incMethod.MakeGenericMethod(tbref.RefEntityType),
|
if (collectionBefore == null && collectionAfter != null)
|
||||||
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(IFreeSql fsql, Type rootEntityType)
|
|
||||||
{
|
|
||||||
return LocalGetAutoIncludeQueryStaicCode(1, rootEntityType, "", new Stack<Type>());
|
|
||||||
string LocalGetAutoIncludeQueryStaicCode(int depth, Type entityType, string navigatePath, Stack<Type> 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())
|
|
||||||
{
|
|
||||||
var tbref = tr.Value;
|
|
||||||
if (tbref.Exception != null) 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:
|
foreach (var item in collectionAfter)
|
||||||
if (ignores.Any(a => a == tbref.RefEntityType)) break;
|
tracking.InsertLog.Add(NativeTuple.Create(elementType, item));
|
||||||
code.Append("\r\n").Append(depthTab).Append(".Include(").Append(lambdaStr).Append(navigateExpression).Append(")");
|
return;
|
||||||
code.Append(LocalGetAutoIncludeQueryStaicCode(depth, tbref.RefEntityType, navigateExpression, ignores));
|
|
||||||
break;
|
|
||||||
case TableRefType.OneToMany:
|
|
||||||
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 (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).Append(")");
|
|
||||||
break;
|
|
||||||
case TableRefType.PgArrayToMany:
|
|
||||||
code.Append("\r\n//").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression).Append(")");
|
|
||||||
break;
|
|
||||||
case TableRefType.ManyToOne: //不属于聚合根
|
|
||||||
code.Append("\r\n//").Append(depthTab).Append(".Include(").Append(lambdaStr).Append(navigateExpression).Append(")");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if (collectionBefore != null && collectionAfter == null)
|
||||||
|
{
|
||||||
|
//foreach (var item in collectionBefore as IEnumerable)
|
||||||
|
//{
|
||||||
|
// changelog.DeleteLog.Add(NativeTuple.Create(elementType, new[] { item }));
|
||||||
|
// NavigateReader(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<string, object> dictBefore = new Dictionary<string, object>();
|
||||||
|
Dictionary<string, object> dictAfter = new Dictionary<string, object>();
|
||||||
|
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) tracking.InsertLog.Add(NativeTuple.Create(elementType, item));
|
||||||
|
else dictAfter.Add(key, 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(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);
|
||||||
}
|
}
|
||||||
ignores.Pop();
|
|
||||||
return code.ToString();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static List<object> GetManyToManyObjects(IFreeSql fsql, TableInfo table, TableRef tbref, object entity, PropertyInfo prop)
|
public static void NavigateReader(IFreeSql fsql, Type rootType, object rootEntity, Action<string, TableRef, Type, List<object>> callback)
|
||||||
{
|
|
||||||
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<object>();
|
|
||||||
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();
|
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
|
||||||
|
var statckPath = new Stack<string>();
|
||||||
|
var stackValues = new List<object>();
|
||||||
|
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<string, bool>());
|
||||||
|
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;
|
||||||
|
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);
|
||||||
|
LocalNavigateReader(tbref.RefEntityType, propval);
|
||||||
|
stackValues.RemoveAt(stackValues.Count - 1);
|
||||||
|
statckPath.Pop();
|
||||||
|
break;
|
||||||
|
case TableRefType.OneToMany:
|
||||||
|
var propvalOtm = table.GetPropertyValue(entity, prop.Name);
|
||||||
|
if (propvalOtm == null) continue;
|
||||||
|
SetNavigateRelationshipValue(fsql, tbref, table.Type, entity, propvalOtm);
|
||||||
|
var propvalOtmList = new List<object>();
|
||||||
|
foreach (var val in propvalOtm as IEnumerable)
|
||||||
|
propvalOtmList.Add(val);
|
||||||
|
statckPath.Push($"{prop.Name}[]");
|
||||||
|
stackValues.Add(propvalOtmList.ToArray());
|
||||||
|
callback?.Invoke(string.Join(".", statckPath), tbref, tbref.RefEntityType, stackValues);
|
||||||
|
foreach (var val in propvalOtm as IEnumerable)
|
||||||
|
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.RefEntityType, stackValues);
|
||||||
|
stackValues.RemoveAt(stackValues.Count - 1);
|
||||||
|
statckPath.Pop();
|
||||||
|
break;
|
||||||
|
case TableRefType.PgArrayToMany:
|
||||||
|
case TableRefType.ManyToOne: //不属于聚合根
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void MapEntityValue(IFreeSql fsql, Type rootEntityType, object rootEntityFrom, object rootEntityTo)
|
||||||
|
{
|
||||||
|
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
|
||||||
|
LocalMapEntityValue(rootEntityType, rootEntityFrom, rootEntityTo);
|
||||||
|
ignores.Clear();
|
||||||
|
|
||||||
|
void LocalMapEntityValue(Type entityType, object entityFrom, object entityTo)
|
||||||
|
{
|
||||||
|
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<string, bool>());
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
var tbref = table.GetTableRef(prop.Name, false);
|
||||||
|
if (tbref == null) 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);
|
||||||
|
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, true);
|
||||||
|
break;
|
||||||
|
case TableRefType.ManyToMany:
|
||||||
|
LocalMapEntityValueCollection(entityType, entityFrom, entityTo, tbref, propvalFrom as IEnumerable, prop, false);
|
||||||
|
break;
|
||||||
|
case TableRefType.PgArrayToMany:
|
||||||
|
case TableRefType.ManyToOne: //不属于聚合根
|
||||||
|
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();
|
||||||
|
if (cascade) LocalMapEntityValue(tbref.RefEntityType, fromItem, toItem);
|
||||||
|
else EntityUtilExtensions.MapEntityValue(fsql, tbref.RefEntityType, fromItem, toItem);
|
||||||
|
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<Type, ConcurrentDictionary<Type, Action<ISelect0>>> _dicGetAutoIncludeQuery = new ConcurrentDictionary<Type, ConcurrentDictionary<Type, Action<ISelect0>>>();
|
||||||
|
public static ISelect<TEntity> GetAutoIncludeQuery<TEntity>(ISelect<TEntity> select)
|
||||||
|
{
|
||||||
|
var select0p = select as Select0Provider;
|
||||||
|
var table0Type = select0p._tables[0].Table.Type;
|
||||||
|
var func = _dicGetAutoIncludeQuery.GetOrAdd(typeof(TEntity), t => new ConcurrentDictionary<Type, Action<ISelect0>>()).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<Type>());
|
||||||
|
return Expression.Lambda<Action<ISelect0>>(exp, parmExp1).Compile();
|
||||||
|
});
|
||||||
|
func(select);
|
||||||
|
return select;
|
||||||
|
Expression LocalGetAutoIncludeQuery(Expression queryExp, int depth, Type entityType, ParameterExpression navigateParameterExp, Expression navigatePathExp, Stack<Type> 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;
|
||||||
|
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);
|
||||||
|
queryExp = LocalGetAutoIncludeQuery(queryExp, depth, tbref.RefEntityType, navigateParameterExp, navigateExp, ignores);
|
||||||
|
break;
|
||||||
|
case TableRefType.OneToMany:
|
||||||
|
LocalIncludeMany(tbref, navigateExp, true);
|
||||||
|
break;
|
||||||
|
case TableRefType.ManyToMany:
|
||||||
|
LocalIncludeMany(tbref, navigateExp, false);
|
||||||
|
break;
|
||||||
|
case TableRefType.PgArrayToMany:
|
||||||
|
break;
|
||||||
|
case TableRefType.ManyToOne: //不属于聚合根
|
||||||
|
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(IFreeSql fsql, Type rootEntityType)
|
||||||
|
{
|
||||||
|
return $"//fsql.Select<{rootEntityType.Name}>()\r\nSelectDiy{LocalGetAutoIncludeQueryStaicCode(1, rootEntityType, "", new Stack<Type>())}";
|
||||||
|
string LocalGetAutoIncludeQueryStaicCode(int depth, Type entityType, string navigatePath, Stack<Type> 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;
|
||||||
|
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(")");
|
||||||
|
code.Append(LocalGetAutoIncludeQueryStaicCode(depth, tbref.RefEntityType, navigateExpression, ignores));
|
||||||
|
break;
|
||||||
|
case TableRefType.OneToMany:
|
||||||
|
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 (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).Append(")");
|
||||||
|
break;
|
||||||
|
case TableRefType.PgArrayToMany:
|
||||||
|
code.Append("\r\n//").Append(depthTab).Append(".IncludeMany(").Append(lambdaStr).Append(navigateExpression).Append(")");
|
||||||
|
break;
|
||||||
|
case TableRefType.ManyToOne: //不属于聚合根
|
||||||
|
code.Append("\r\n//").Append(depthTab).Append(".Include(").Append(lambdaStr).Append(navigateExpression).Append(")");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ignores.Pop();
|
||||||
|
return code.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<object> 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<object>();
|
||||||
|
var leftpkvals = new object[tbref.Columns.Count];
|
||||||
for (var x = 0; x < tbref.Columns.Count; x++)
|
for (var x = 0; x < tbref.Columns.Count; x++)
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, tbref.RefMiddleEntityType, midval, tbref.MiddleColumns[x].CsName, leftpkvals[x]);
|
leftpkvals[x] = Utils.GetDataReaderValue(tbref.MiddleColumns[x].CsType, EntityUtilExtensions.GetEntityValueWithPropertyName(fsql, table.Type, entity, tbref.Columns[x].CsName));
|
||||||
|
foreach (var right in rights)
|
||||||
for (var x = tbref.Columns.Count; x < tbref.MiddleColumns.Count; x++)
|
|
||||||
{
|
{
|
||||||
var refcol = tbref.RefColumns[x - tbref.Columns.Count];
|
var midval = tbref.RefMiddleEntityType.CreateInstanceGetDefaultValue();
|
||||||
var refval = EntityUtilExtensions.GetEntityValueWithPropertyName(fsql, tbref.RefEntityType, right, refcol.CsName);
|
for (var x = 0; x < tbref.Columns.Count; x++)
|
||||||
if (refval == refcol.CsType.CreateInstanceGetDefaultValue()) throw new Exception($"ManyToMany 关联对象的主键属性({tbref.RefEntityType.DisplayCsharp()}.{refcol.CsName})不能为空");
|
EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, tbref.RefMiddleEntityType, midval, tbref.MiddleColumns[x].CsName, leftpkvals[x]);
|
||||||
refval = Utils.GetDataReaderValue(tbref.MiddleColumns[x].CsType, refval);
|
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, tbref.RefMiddleEntityType, midval, tbref.MiddleColumns[x].CsName, refval);
|
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);
|
||||||
}
|
}
|
||||||
middles.Add(midval);
|
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;
|
|
||||||
switch (tbref.RefType)
|
|
||||||
{
|
{
|
||||||
case TableRefType.OneToOne:
|
if (rightItem == null) return;
|
||||||
for (var idx = 0; idx < tbref.Columns.Count; idx++)
|
switch (tbref.RefType)
|
||||||
{
|
{
|
||||||
var colval = Utils.GetDataReaderValue(tbref.RefColumns[idx].CsType, EntityUtilExtensions.GetEntityValueWithPropertyName(orm, leftType, leftItem, tbref.Columns[idx].CsName));
|
case TableRefType.OneToOne:
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(orm, tbref.RefEntityType, rightItem, tbref.RefColumns[idx].CsName, colval);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TableRefType.OneToMany:
|
|
||||||
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++)
|
for (var idx = 0; idx < tbref.Columns.Count; idx++)
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(orm, tbref.RefEntityType, rightEle, tbref.RefColumns[idx].CsName, leftColValsOtm[idx]);
|
{
|
||||||
break;
|
var colval = Utils.GetDataReaderValue(tbref.RefColumns[idx].CsType, EntityUtilExtensions.GetEntityValueWithPropertyName(orm, leftType, leftItem, tbref.Columns[idx].CsName));
|
||||||
case TableRefType.ManyToOne:
|
EntityUtilExtensions.SetEntityValueWithPropertyName(orm, tbref.RefEntityType, rightItem, tbref.RefColumns[idx].CsName, colval);
|
||||||
for (var idx = 0; idx < tbref.RefColumns.Count; idx++)
|
}
|
||||||
{
|
break;
|
||||||
var colval = Utils.GetDataReaderValue(tbref.Columns[idx].CsType, EntityUtilExtensions.GetEntityValueWithPropertyName(orm, tbref.RefEntityType, rightItem, tbref.RefColumns[idx].CsName));
|
case TableRefType.OneToMany:
|
||||||
EntityUtilExtensions.SetEntityValueWithPropertyName(orm, leftType, leftItem, tbref.Columns[idx].CsName, colval);
|
var rightEachOtm = rightItem as IEnumerable;
|
||||||
}
|
if (rightEachOtm == null) break;
|
||||||
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 = 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,7 +14,6 @@ namespace FreeSql.Tests.DbContext2
|
|||||||
{
|
{
|
||||||
public UserRepository(IFreeSql fsql, UnitOfWorkManager uowManager) : base(uowManager?.Orm ?? fsql)
|
public UserRepository(IFreeSql fsql, UnitOfWorkManager uowManager) : base(uowManager?.Orm ?? fsql)
|
||||||
{
|
{
|
||||||
var code = SelectAggregateRootStaticCode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ISelect<User> Select => base.SelectDiy;
|
public override ISelect<User> Select => base.SelectDiy;
|
||||||
@ -27,6 +26,11 @@ namespace FreeSql.Tests.DbContext2
|
|||||||
{
|
{
|
||||||
new UserRepository(fsql, null);
|
new UserRepository(fsql, null);
|
||||||
|
|
||||||
|
var code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(fsql, typeof(User));
|
||||||
|
Assert.Equal(@"//fsql.Select<User>()
|
||||||
|
SelectDiy
|
||||||
|
.Include(a => a.Ext)", code);
|
||||||
|
|
||||||
var repo = fsql.GetAggregateRootRepository<User>();
|
var repo = fsql.GetAggregateRootRepository<User>();
|
||||||
var user = new User
|
var user = new User
|
||||||
{
|
{
|
||||||
@ -93,6 +97,48 @@ namespace FreeSql.Tests.DbContext2
|
|||||||
|
|
||||||
user.Ext.Remark = "admin01_remark changed01";
|
user.Ext.Remark = "admin01_remark changed01";
|
||||||
repo.Update(user);
|
repo.Update(user);
|
||||||
|
user = repo.Where(a => a.Id == 1).First();
|
||||||
|
Assert.NotNull(user);
|
||||||
|
Assert.Equal(1, user.Id);
|
||||||
|
Assert.Equal("admin01", user.UserName);
|
||||||
|
Assert.Equal("admin01_pwd", user.Password);
|
||||||
|
Assert.NotNull(user.Ext);
|
||||||
|
Assert.Equal(1, user.Ext.UserId);
|
||||||
|
Assert.Equal("admin01_remark changed01", user.Ext.Remark);
|
||||||
|
|
||||||
|
var affrows = repo.Delete(user);
|
||||||
|
Assert.Equal(2, affrows);
|
||||||
|
Assert.False(fsql.Select<User>().Where(a => a.Id == 1).Any());
|
||||||
|
Assert.False(fsql.Select<UserExt>().Where(a => a.UserId == 1).Any());
|
||||||
|
|
||||||
|
var deleted = repo.DeleteCascadeByDatabase(a => a.Id == 2 || a.Id == 3);
|
||||||
|
Assert.NotNull(deleted);
|
||||||
|
Assert.Equal(4, deleted.Count);
|
||||||
|
Assert.False(fsql.Select<User>().Where(a => a.Id == 2).Any());
|
||||||
|
Assert.False(fsql.Select<UserExt>().Where(a => a.UserId == 2).Any());
|
||||||
|
Assert.False(fsql.Select<User>().Where(a => a.Id == 3).Any());
|
||||||
|
Assert.False(fsql.Select<UserExt>().Where(a => a.UserId == 3).Any());
|
||||||
|
users = new[]
|
||||||
|
{
|
||||||
|
(User)deleted[3],
|
||||||
|
(User)deleted[1],
|
||||||
|
};
|
||||||
|
users[0].Ext = (UserExt)deleted[2];
|
||||||
|
users[1].Ext = (UserExt)deleted[0];
|
||||||
|
Assert.Equal(2, users.Length);
|
||||||
|
Assert.Equal(2, users[0].Id);
|
||||||
|
Assert.Equal("admin02", users[0].UserName);
|
||||||
|
Assert.Equal("admin02_pwd", users[0].Password);
|
||||||
|
Assert.NotNull(users[0].Ext);
|
||||||
|
Assert.Equal(2, users[0].Ext.UserId);
|
||||||
|
Assert.Equal("admin02_remark", users[0].Ext.Remark);
|
||||||
|
Assert.Equal(3, users[1].Id);
|
||||||
|
Assert.Equal("admin03", users[1].UserName);
|
||||||
|
Assert.Equal("admin03_pwd", users[1].Password);
|
||||||
|
Assert.NotNull(users[1].Ext);
|
||||||
|
Assert.Equal(3, users[1].Ext.UserId);
|
||||||
|
Assert.Equal("admin03_remark", users[1].Ext.Remark);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class User
|
class User
|
||||||
|
@ -15,7 +15,6 @@ namespace FreeSql.Tests.DbContext2
|
|||||||
{
|
{
|
||||||
public OrderRepository(IFreeSql fsql, UnitOfWorkManager uowManager) : base(uowManager?.Orm ?? fsql)
|
public OrderRepository(IFreeSql fsql, UnitOfWorkManager uowManager) : base(uowManager?.Orm ?? fsql)
|
||||||
{
|
{
|
||||||
var code = SelectAggregateRootStaticCode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ISelect<Order> Select => base.SelectDiy;
|
public override ISelect<Order> Select => base.SelectDiy;
|
||||||
@ -28,6 +27,14 @@ namespace FreeSql.Tests.DbContext2
|
|||||||
{
|
{
|
||||||
new OrderRepository(fsql, null);
|
new OrderRepository(fsql, null);
|
||||||
|
|
||||||
|
var code = AggregateRootUtils.GetAutoIncludeQueryStaicCode(fsql, typeof(Order));
|
||||||
|
Assert.Equal(@"//fsql.Select<Order>()
|
||||||
|
SelectDiy
|
||||||
|
.IncludeMany(a => a.Details, then => then
|
||||||
|
.Include(b => b.Extdata))
|
||||||
|
.IncludeMany(a => a.Tags)
|
||||||
|
.Include(a => a.Extdata)", code);
|
||||||
|
|
||||||
fsql.Insert(new[]
|
fsql.Insert(new[]
|
||||||
{
|
{
|
||||||
new Tag { Name = "tag1" },
|
new Tag { Name = "tag1" },
|
||||||
|
Loading…
x
Reference in New Issue
Block a user