mirror of
https://github.com/nsnail/FreeSql.git
synced 2025-04-22 10:42:52 +08:00
AggregateRootRepository
This commit is contained in:
parent
5bbbeb16c2
commit
0f41b70e7e
@ -17,23 +17,247 @@ namespace FreeSql
|
||||
{
|
||||
partial class AggregateRootRepository<TEntity>
|
||||
{
|
||||
//以下临时调用同步方法
|
||||
public Task<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default) => Task.FromResult(Insert(entity));
|
||||
public Task<List<TEntity>> InsertAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => Task.FromResult(Insert(entitys));
|
||||
public Task<TEntity> InsertOrUpdateAsync(TEntity entity, CancellationToken cancellationToken = default) => Task.FromResult(InsertOrUpdate(entity));
|
||||
async public Task SaveManyAsync(TEntity entity, string propertyName, CancellationToken cancellationToken = default)
|
||||
|
||||
#region InsertAsync
|
||||
async public virtual Task<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default) => (await InsertAsync(new[] { entity }, cancellationToken)).FirstOrDefault();
|
||||
async public virtual Task<List<TEntity>> InsertAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default)
|
||||
{
|
||||
SaveMany(entity, propertyName);
|
||||
var repos = new Dictionary<Type, object>();
|
||||
try
|
||||
{
|
||||
var ret = await InsertWithinBoundaryStaticAsync(_repository, GetChildRepository, entitys, out var affrows, cancellationToken);
|
||||
Attach(ret);
|
||||
return ret;
|
||||
}
|
||||
finally
|
||||
{
|
||||
DisposeChildRepositorys();
|
||||
_repository.FlushState();
|
||||
}
|
||||
}
|
||||
Task<List<T1>> InsertWithinBoundaryStaticAsync<T1>(IBaseRepository<T1> rootRepository, Func<Type, IBaseRepository<object>> getChildRepository, IEnumerable<T1> rootEntitys, out int affrows, CancellationToken cancellationToken) where T1 : class
|
||||
{
|
||||
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 LocalInsertAsync(rootRepository, rootEntitys);
|
||||
}
|
||||
finally
|
||||
{
|
||||
affrows = localAffrows;
|
||||
}
|
||||
|
||||
bool LocalCanInsert(Type entityType, object entity, bool isadd)
|
||||
{
|
||||
var stateKey = rootRepository.Orm.GetEntityKeyString(entityType, entity, false);
|
||||
if (stateKey == null) return true;
|
||||
if (ignores.TryGetValue(entityType, out var stateKeys) == false)
|
||||
{
|
||||
if (isadd)
|
||||
{
|
||||
ignores.Add(entityType, stateKeys = new Dictionary<string, bool>());
|
||||
stateKeys.Add(stateKey, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (stateKeys.ContainsKey(stateKey) == false)
|
||||
{
|
||||
if (isadd) stateKeys.Add(stateKey, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
async Task<List<T2>> LocalInsertAsync<T2>(IBaseRepository<T2> repository, IEnumerable<T2> entitys) where T2 : class
|
||||
{
|
||||
var table = repository.Orm.CodeFirst.GetTableByEntity(repository.EntityType);
|
||||
if (table.Primarys.Any(col => col.Attribute.IsIdentity))
|
||||
{
|
||||
foreach (var entity in entitys)
|
||||
repository.Orm.ClearEntityPrimaryValueWithIdentity(repository.EntityType, entity);
|
||||
}
|
||||
var ret = await repository.InsertAsync(entitys, cancellationToken);
|
||||
localAffrows += ret.Count;
|
||||
foreach (var entity in entitys) LocalCanInsert(repository.EntityType, entity, true);
|
||||
|
||||
foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key))
|
||||
{
|
||||
var tbref = tr.Value;
|
||||
if (tbref.Exception != null) continue;
|
||||
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue;
|
||||
switch (tbref.RefType)
|
||||
{
|
||||
case TableRefType.OneToOne:
|
||||
var otoList = ret.Select(entity =>
|
||||
{
|
||||
var otoItem = table.GetPropertyValue(entity, prop.Name);
|
||||
if (LocalCanInsert(tbref.RefEntityType, otoItem, false) == false) return null;
|
||||
AggregateRootUtils.SetNavigateRelationshipValue(repository.Orm, tbref, table.Type, entity, otoItem);
|
||||
return otoItem;
|
||||
}).Where(entity => entity != null).ToArray();
|
||||
if (otoList.Any())
|
||||
{
|
||||
var repo = getChildRepository(tbref.RefEntityType);
|
||||
await LocalInsertAsync(repo, otoList);
|
||||
}
|
||||
break;
|
||||
case TableRefType.OneToMany:
|
||||
var otmList = ret.Select(entity =>
|
||||
{
|
||||
var otmEach = table.GetPropertyValue(entity, prop.Name) as IEnumerable;
|
||||
if (otmEach == null) return null;
|
||||
var otmItems = new List<object>();
|
||||
foreach (var otmItem in otmEach)
|
||||
{
|
||||
if (LocalCanInsert(tbref.RefEntityType, otmItem, false) == false) continue;
|
||||
otmItems.Add(otmItem);
|
||||
}
|
||||
AggregateRootUtils.SetNavigateRelationshipValue(repository.Orm, tbref, table.Type, entity, otmItems);
|
||||
return otmItems;
|
||||
}).Where(entity => entity != null).SelectMany(entity => entity).ToArray();
|
||||
if (otmList.Any())
|
||||
{
|
||||
var repo = getChildRepository(tbref.RefEntityType);
|
||||
await LocalInsertAsync(repo, otmList);
|
||||
}
|
||||
break;
|
||||
case TableRefType.ManyToMany:
|
||||
var mtmMidList = new List<object>();
|
||||
ret.ForEach(entity =>
|
||||
{
|
||||
var mids = AggregateRootUtils.GetManyToManyObjects(repository.Orm, table, tbref, entity, prop);
|
||||
if (mids != null) mtmMidList.AddRange(mids);
|
||||
});
|
||||
if (mtmMidList.Any())
|
||||
{
|
||||
var repo = getChildRepository(tbref.RefMiddleEntityType);
|
||||
await LocalInsertAsync(repo, mtmMidList);
|
||||
}
|
||||
break;
|
||||
case TableRefType.PgArrayToMany:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
async public virtual Task<TEntity> InsertOrUpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
||||
if (entity == null) throw new ArgumentNullException(nameof(entity));
|
||||
var table = Orm.CodeFirst.GetTableByEntity(EntityType);
|
||||
if (table.Primarys.Any() == false) throw new Exception(DbContextStrings.CannotAdd_EntityHasNo_PrimaryKey(Orm.GetEntityString(EntityType, entity)));
|
||||
|
||||
var flagExists = ExistsInStates(entity);
|
||||
if (flagExists == false)
|
||||
{
|
||||
var olddata = await Select.WhereDynamic(entity).FirstAsync(cancellationToken);
|
||||
flagExists = olddata != null;
|
||||
}
|
||||
if (flagExists == true)
|
||||
{
|
||||
var affrows = await UpdateAsync(entity, cancellationToken);
|
||||
if (affrows > 0) return entity;
|
||||
}
|
||||
if (table.Primarys.Where(a => a.Attribute.IsIdentity).Count() == table.Primarys.Length)
|
||||
{
|
||||
Orm.ClearEntityPrimaryValueWithIdentity(EntityType, entity);
|
||||
return await InsertAsync(entity, cancellationToken);
|
||||
}
|
||||
throw new Exception(DbContextStrings.CannotAdd_PrimaryKey_NotSet(Orm.GetEntityString(EntityType, entity)));
|
||||
}
|
||||
|
||||
public Task<int> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default) => Task.FromResult(Update(entity));
|
||||
public Task<int> UpdateAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => Task.FromResult(Update(entitys));
|
||||
public virtual Task<int> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default) => UpdateAsync(new[] { entity }, cancellationToken);
|
||||
public virtual Task<int> UpdateAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var tracking = new AggregateRootTrackingChangeInfo();
|
||||
foreach (var entity in entitys)
|
||||
{
|
||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
||||
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以更新数据 {Orm.GetEntityString(EntityType, entity)}");
|
||||
AggregateRootUtils.CompareEntityValue(Orm, EntityType, state.Value, entity, null, tracking);
|
||||
}
|
||||
foreach (var entity in entitys)
|
||||
Attach(entity);
|
||||
|
||||
public Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default) => Task.FromResult(Delete(entity));
|
||||
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) => Task.FromResult(Delete(predicate));
|
||||
public Task<List<object>> DeleteCascadeByDatabaseAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => Task.FromResult(DeleteCascadeByDatabase(predicate));
|
||||
return SaveTrackingChangeAsync(tracking, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
public virtual Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default) => DeleteWithinBoundaryAsync(new[] { entity }, null, cancellationToken);
|
||||
public virtual Task<int> DeleteAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => DeleteWithinBoundaryAsync(entitys, null, cancellationToken);
|
||||
async public virtual Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => await DeleteWithinBoundaryAsync(await SelectAggregateRoot.Where(predicate).ToListAsync(cancellationToken), null, cancellationToken);
|
||||
async public virtual Task<List<object>> DeleteCascadeByDatabaseAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var deletedOutput = new List<object>();
|
||||
await DeleteWithinBoundaryAsync(await SelectAggregateRoot.Where(predicate).ToListAsync(cancellationToken), deletedOutput, cancellationToken);
|
||||
return deletedOutput;
|
||||
}
|
||||
async Task<int> DeleteWithinBoundaryAsync(IEnumerable<TEntity> entitys, List<object> deletedOutput, CancellationToken cancellationToken)
|
||||
{
|
||||
var tracking = new AggregateRootTrackingChangeInfo();
|
||||
foreach (var entity in entitys)
|
||||
{
|
||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
||||
AggregateRootUtils.CompareEntityValue(Orm, EntityType, entity, null, null, tracking);
|
||||
_states.Remove(stateKey);
|
||||
}
|
||||
var affrows = 0;
|
||||
for (var a = tracking.DeleteLog.Count - 1; a >= 0; a--)
|
||||
{
|
||||
affrows += await Orm.Delete<object>().AsType(tracking.DeleteLog[a].Item1).AsTable(_asTableRule)
|
||||
.WhereDynamic(tracking.DeleteLog[a].Item2).ExecuteAffrowsAsync(cancellationToken);
|
||||
if (deletedOutput != null) deletedOutput.AddRange(tracking.DeleteLog[a].Item2);
|
||||
}
|
||||
return affrows;
|
||||
}
|
||||
|
||||
async public virtual Task SaveManyAsync(TEntity entity, string propertyName, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var tracking = new AggregateRootTrackingChangeInfo();
|
||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
||||
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以保存数据 {Orm.GetEntityString(EntityType, entity)}");
|
||||
AggregateRootUtils.CompareEntityValue(Orm, EntityType, state.Value, entity, propertyName, tracking);
|
||||
Attach(entity); //应该只存储 propertyName 内容
|
||||
await SaveTrackingChangeAsync(tracking, cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
async Task<int> SaveTrackingChangeAsync(AggregateRootTrackingChangeInfo tracking, CancellationToken cancellationToken)
|
||||
{
|
||||
var affrows = 0;
|
||||
DisposeChildRepositorys();
|
||||
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)
|
||||
{
|
||||
var repo = GetChildRepository(il.Key);
|
||||
await InsertWithinBoundaryStaticAsync(repo, GetChildRepository, il.Value, out var affrowsOut, cancellationToken);
|
||||
affrows += affrowsOut;
|
||||
}
|
||||
|
||||
for (var a = tracking.DeleteLog.Count - 1; a >= 0; a--)
|
||||
affrows += await Orm.Delete<object>().AsType(tracking.DeleteLog[a].Item1).AsTable(_asTableRule)
|
||||
.WhereDynamic(tracking.DeleteLog[a].Item2).ExecuteAffrowsAsync(cancellationToken);
|
||||
|
||||
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());
|
||||
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 dl2 in dl.Value)
|
||||
{
|
||||
affrows += await Orm.Update<object>().AsType(dl.Key).AsTable(_asTableRule)
|
||||
.SetSource(dl2.Value.Select(a => a.Item2).ToArray())
|
||||
.UpdateColumns(dl2.Value.First().Item4.ToArray())
|
||||
.ExecuteAffrowsAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
DisposeChildRepositorys();
|
||||
return affrows;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -15,242 +15,11 @@ namespace FreeSql
|
||||
{
|
||||
partial class AggregateRootRepository<TEntity>
|
||||
{
|
||||
public TEntity Insert(TEntity entity) => InsertAggregateRoot(new[] { entity }).FirstOrDefault();
|
||||
public List<TEntity> Insert(IEnumerable<TEntity> entitys) => InsertAggregateRoot(entitys);
|
||||
public TEntity InsertOrUpdate(TEntity entity) => InsertOrUpdateAggregateRoot(entity);
|
||||
public int Update(TEntity entity) => UpdateAggregateRoot(new[] { entity });
|
||||
public int Update(IEnumerable<TEntity> entitys) => UpdateAggregateRoot(entitys);
|
||||
public int Delete(TEntity entity) => DeleteAggregateRoot(new[] { entity });
|
||||
public int Delete(IEnumerable<TEntity> entitys) => DeleteAggregateRoot(entitys);
|
||||
public int Delete(Expression<Func<TEntity, bool>> predicate) => DeleteAggregateRoot(SelectAggregateRoot.Where(predicate).ToList());
|
||||
public List<object> DeleteCascadeByDatabase(Expression<Func<TEntity, bool>> predicate)
|
||||
{
|
||||
var deletedOutput = new List<object>();
|
||||
DeleteAggregateRoot(SelectAggregateRoot.Where(predicate).ToList(), deletedOutput);
|
||||
return deletedOutput;
|
||||
}
|
||||
public void SaveMany(TEntity entity, string propertyName) => SaveManyAggregateRoot(entity, propertyName);
|
||||
|
||||
protected virtual List<TEntity> InsertAggregateRoot(IEnumerable<TEntity> entitys)
|
||||
{
|
||||
var repos = new Dictionary<Type, object>();
|
||||
try
|
||||
{
|
||||
var ret = InsertAggregateRootStatic(_repository, GetChildRepository, entitys, out var affrows);
|
||||
Attach(ret);
|
||||
return ret;
|
||||
}
|
||||
finally
|
||||
{
|
||||
DisposeChildRepositorys();
|
||||
_repository.FlushState();
|
||||
}
|
||||
}
|
||||
protected static List<T1> InsertAggregateRootStatic<T1>(IBaseRepository<T1> rootRepository, Func<Type, IBaseRepository<object>> getChildRepository, IEnumerable<T1> rootEntitys, out int affrows) where T1 : class {
|
||||
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
|
||||
Dictionary<Type, IBaseRepository<object>> repos = new Dictionary<Type, IBaseRepository<object>>();
|
||||
var localAffrows = 0;
|
||||
try
|
||||
{
|
||||
return LocalInsertAggregateRoot(rootRepository, rootEntitys);
|
||||
}
|
||||
finally
|
||||
{
|
||||
affrows = localAffrows;
|
||||
}
|
||||
|
||||
bool LocalCanAggregateRoot(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;
|
||||
}
|
||||
List<T2> LocalInsertAggregateRoot<T2>(IBaseRepository<T2> repository, IEnumerable<T2> entitys) where T2 : class
|
||||
{
|
||||
var table = repository.Orm.CodeFirst.GetTableByEntity(repository.EntityType);
|
||||
if (table.Primarys.Any(col => col.Attribute.IsIdentity))
|
||||
{
|
||||
foreach (var entity in entitys)
|
||||
repository.Orm.ClearEntityPrimaryValueWithIdentity(repository.EntityType, entity);
|
||||
}
|
||||
var ret = repository.Insert(entitys);
|
||||
localAffrows += ret.Count;
|
||||
foreach (var entity in entitys) LocalCanAggregateRoot(repository.EntityType, entity, true);
|
||||
|
||||
foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key))
|
||||
{
|
||||
var tbref = tr.Value;
|
||||
if (tbref.Exception != null) continue;
|
||||
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue;
|
||||
switch (tbref.RefType)
|
||||
{
|
||||
case TableRefType.OneToOne:
|
||||
var otoList = ret.Select(entity =>
|
||||
{
|
||||
var otoItem = table.GetPropertyValue(entity, prop.Name);
|
||||
if (LocalCanAggregateRoot(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);
|
||||
LocalInsertAggregateRoot(repo, otoList);
|
||||
}
|
||||
break;
|
||||
case TableRefType.OneToMany:
|
||||
var otmList = ret.Select(entity =>
|
||||
{
|
||||
var otmEach = table.GetPropertyValue(entity, prop.Name) as IEnumerable;
|
||||
if (otmEach == null) return null;
|
||||
var otmItems = new List<object>();
|
||||
foreach (var otmItem in otmEach)
|
||||
{
|
||||
if (LocalCanAggregateRoot(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);
|
||||
LocalInsertAggregateRoot(repo, otmList);
|
||||
}
|
||||
break;
|
||||
case TableRefType.ManyToMany:
|
||||
var mtmMidList = new List<object>();
|
||||
ret.ForEach(entity =>
|
||||
{
|
||||
var mids = AggregateRootUtils.GetManyToManyObjects(repository.Orm, table, tbref, entity, prop);
|
||||
if (mids != null) mtmMidList.AddRange(mids);
|
||||
});
|
||||
if (mtmMidList.Any())
|
||||
{
|
||||
var repo = getChildRepository(tbref.RefMiddleEntityType);
|
||||
LocalInsertAggregateRoot(repo, mtmMidList);
|
||||
}
|
||||
break;
|
||||
case TableRefType.PgArrayToMany:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual TEntity InsertOrUpdateAggregateRoot(TEntity entity)
|
||||
{
|
||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
||||
if (entity == null) throw new ArgumentNullException(nameof(entity));
|
||||
var table = Orm.CodeFirst.GetTableByEntity(EntityType);
|
||||
if (table.Primarys.Any() == false) throw new Exception(DbContextStrings.CannotAdd_EntityHasNo_PrimaryKey(Orm.GetEntityString(EntityType, entity)));
|
||||
|
||||
var flagExists = ExistsInStates(entity);
|
||||
if (flagExists == false)
|
||||
{
|
||||
var olddata = Select.WhereDynamic(entity).First();
|
||||
flagExists = olddata != null;
|
||||
}
|
||||
if (flagExists == true)
|
||||
{
|
||||
var affrows = UpdateAggregateRoot(new[] { entity });
|
||||
if (affrows > 0) return entity;
|
||||
}
|
||||
if (table.Primarys.Where(a => a.Attribute.IsIdentity).Count() == table.Primarys.Length)
|
||||
{
|
||||
Orm.ClearEntityPrimaryValueWithIdentity(EntityType, entity);
|
||||
return InsertAggregateRoot(new[] { entity }).FirstOrDefault();
|
||||
}
|
||||
throw new Exception(DbContextStrings.CannotAdd_PrimaryKey_NotSet(Orm.GetEntityString(EntityType, entity)));
|
||||
}
|
||||
protected virtual int UpdateAggregateRoot(IEnumerable<TEntity> entitys)
|
||||
{
|
||||
var tracking = new AggregateRootTrackingChangeInfo();
|
||||
foreach(var entity in entitys)
|
||||
{
|
||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
||||
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以更新数据 {Orm.GetEntityString(EntityType, entity)}");
|
||||
AggregateRootUtils.CompareEntityValue(Orm, EntityType, state.Value, entity, null, tracking);
|
||||
}
|
||||
var affrows = 0;
|
||||
DisposeChildRepositorys();
|
||||
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)
|
||||
{
|
||||
var repo = GetChildRepository(il.Key);
|
||||
InsertAggregateRootStatic(repo, GetChildRepository, il.Value, out var affrowsOut);
|
||||
affrows += affrowsOut;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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());
|
||||
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 dl2 in dl.Value)
|
||||
{
|
||||
affrows += Orm.Update<object>().AsType(dl.Key).AsTable(_asTableRule)
|
||||
.SetSource(dl2.Value.Select(a => a.Item2).ToArray())
|
||||
.UpdateColumns(dl2.Value.First().Item4.ToArray())
|
||||
.ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
DisposeChildRepositorys();
|
||||
foreach (var entity in entitys)
|
||||
Attach(entity);
|
||||
|
||||
return affrows;
|
||||
}
|
||||
protected virtual int DeleteAggregateRoot(IEnumerable<TEntity> entitys, List<object> deletedOutput = null)
|
||||
{
|
||||
var tracking = new AggregateRootTrackingChangeInfo();
|
||||
foreach (var entity in entitys)
|
||||
{
|
||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
||||
AggregateRootUtils.CompareEntityValue(Orm, EntityType, entity, null, null, tracking);
|
||||
_states.Remove(stateKey);
|
||||
}
|
||||
var affrows = 0;
|
||||
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)
|
||||
{
|
||||
var tracking = new AggregateRootTrackingChangeInfo();
|
||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
||||
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以保存数据 {Orm.GetEntityString(EntityType, entity)}");
|
||||
AggregateRootUtils.CompareEntityValue(Orm, EntityType, state.Value, entity, propertyName, tracking);
|
||||
Attach(entity);
|
||||
}
|
||||
|
||||
protected List<TEntity> _dataEditing;
|
||||
protected ConcurrentDictionary<string, EntityState> _statesEditing = new ConcurrentDictionary<string, EntityState>();
|
||||
public void BeginEdit(List<TEntity> data)
|
||||
#region BeginEdit/EndEdit
|
||||
List<TEntity> _dataEditing;
|
||||
ConcurrentDictionary<string, EntityState> _statesEditing = new ConcurrentDictionary<string, EntityState>();
|
||||
public virtual void BeginEdit(List<TEntity> data)
|
||||
{
|
||||
if (data == null) return;
|
||||
var table = Orm.CodeFirst.GetTableByEntity(EntityType);
|
||||
@ -270,7 +39,7 @@ namespace FreeSql
|
||||
});
|
||||
}
|
||||
}
|
||||
public int EndEdit(List<TEntity> data = null)
|
||||
public virtual int EndEdit(List<TEntity> data = null)
|
||||
{
|
||||
if (data == null) data = _dataEditing;
|
||||
if (data == null) return 0;
|
||||
@ -293,15 +62,253 @@ namespace FreeSql
|
||||
foreach (var item in _statesEditing.Values.OrderBy(a => a.Time))
|
||||
AggregateRootUtils.CompareEntityValue(Orm, EntityType, item, null, null, tracking);
|
||||
|
||||
|
||||
return SaveTrackingChange(tracking);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_dataEditing = null;
|
||||
_statesEditing.Clear();
|
||||
}
|
||||
return tracking.InsertLog.Count + tracking.UpdateLog.Count + tracking.DeleteLog.Count;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Insert
|
||||
public virtual TEntity Insert(TEntity entity) => Insert(new[] { entity }).FirstOrDefault();
|
||||
public virtual List<TEntity> Insert(IEnumerable<TEntity> entitys)
|
||||
{
|
||||
var repos = new Dictionary<Type, object>();
|
||||
try
|
||||
{
|
||||
var ret = InsertWithinBoundaryStatic(_repository, GetChildRepository, entitys, out var affrows);
|
||||
Attach(ret);
|
||||
return ret;
|
||||
}
|
||||
finally
|
||||
{
|
||||
DisposeChildRepositorys();
|
||||
_repository.FlushState();
|
||||
}
|
||||
}
|
||||
static List<T1> InsertWithinBoundaryStatic<T1>(IBaseRepository<T1> rootRepository, Func<Type, IBaseRepository<object>> getChildRepository, IEnumerable<T1> rootEntitys, out int affrows) where T1 : class {
|
||||
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
|
||||
Dictionary<Type, IBaseRepository<object>> repos = new Dictionary<Type, IBaseRepository<object>>();
|
||||
var localAffrows = 0;
|
||||
try
|
||||
{
|
||||
return LocalInsert(rootRepository, rootEntitys);
|
||||
}
|
||||
finally
|
||||
{
|
||||
affrows = localAffrows;
|
||||
}
|
||||
|
||||
bool LocalCanInsert(Type entityType, object entity, bool isadd)
|
||||
{
|
||||
var stateKey = rootRepository.Orm.GetEntityKeyString(entityType, entity, false);
|
||||
if (stateKey == null) return true;
|
||||
if (ignores.TryGetValue(entityType, out var stateKeys) == false)
|
||||
{
|
||||
if (isadd)
|
||||
{
|
||||
ignores.Add(entityType, stateKeys = new Dictionary<string, bool>());
|
||||
stateKeys.Add(stateKey, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (stateKeys.ContainsKey(stateKey) == false)
|
||||
{
|
||||
if (isadd) stateKeys.Add(stateKey, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
List<T2> LocalInsert<T2>(IBaseRepository<T2> repository, IEnumerable<T2> entitys) where T2 : class
|
||||
{
|
||||
var table = repository.Orm.CodeFirst.GetTableByEntity(repository.EntityType);
|
||||
if (table.Primarys.Any(col => col.Attribute.IsIdentity))
|
||||
{
|
||||
foreach (var entity in entitys)
|
||||
repository.Orm.ClearEntityPrimaryValueWithIdentity(repository.EntityType, entity);
|
||||
}
|
||||
var ret = repository.Insert(entitys);
|
||||
localAffrows += ret.Count;
|
||||
foreach (var entity in entitys) LocalCanInsert(repository.EntityType, entity, true);
|
||||
|
||||
foreach (var tr in table.GetAllTableRef().OrderBy(a => a.Value.RefType).ThenBy(a => a.Key))
|
||||
{
|
||||
var tbref = tr.Value;
|
||||
if (tbref.Exception != null) continue;
|
||||
if (table.Properties.TryGetValue(tr.Key, out var prop) == false) continue;
|
||||
switch (tbref.RefType)
|
||||
{
|
||||
case TableRefType.OneToOne:
|
||||
var otoList = ret.Select(entity =>
|
||||
{
|
||||
var otoItem = table.GetPropertyValue(entity, prop.Name);
|
||||
if (LocalCanInsert(tbref.RefEntityType, otoItem, false) == false) return null;
|
||||
AggregateRootUtils.SetNavigateRelationshipValue(repository.Orm, tbref, table.Type, entity, otoItem);
|
||||
return otoItem;
|
||||
}).Where(entity => entity != null).ToArray();
|
||||
if (otoList.Any())
|
||||
{
|
||||
var repo = getChildRepository(tbref.RefEntityType);
|
||||
LocalInsert(repo, otoList);
|
||||
}
|
||||
break;
|
||||
case TableRefType.OneToMany:
|
||||
var otmList = ret.Select(entity =>
|
||||
{
|
||||
var otmEach = table.GetPropertyValue(entity, prop.Name) as IEnumerable;
|
||||
if (otmEach == null) return null;
|
||||
var otmItems = new List<object>();
|
||||
foreach (var otmItem in otmEach)
|
||||
{
|
||||
if (LocalCanInsert(tbref.RefEntityType, otmItem, false) == false) continue;
|
||||
otmItems.Add(otmItem);
|
||||
}
|
||||
AggregateRootUtils.SetNavigateRelationshipValue(repository.Orm, tbref, table.Type, entity, otmItems);
|
||||
return otmItems;
|
||||
}).Where(entity => entity != null).SelectMany(entity => entity).ToArray();
|
||||
if (otmList.Any())
|
||||
{
|
||||
var repo = getChildRepository(tbref.RefEntityType);
|
||||
LocalInsert(repo, otmList);
|
||||
}
|
||||
break;
|
||||
case TableRefType.ManyToMany:
|
||||
var mtmMidList = new List<object>();
|
||||
ret.ForEach(entity =>
|
||||
{
|
||||
var mids = AggregateRootUtils.GetManyToManyObjects(repository.Orm, table, tbref, entity, prop);
|
||||
if (mids != null) mtmMidList.AddRange(mids);
|
||||
});
|
||||
if (mtmMidList.Any())
|
||||
{
|
||||
var repo = getChildRepository(tbref.RefMiddleEntityType);
|
||||
LocalInsert(repo, mtmMidList);
|
||||
}
|
||||
break;
|
||||
case TableRefType.PgArrayToMany:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public virtual TEntity InsertOrUpdate(TEntity entity)
|
||||
{
|
||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
||||
if (entity == null) throw new ArgumentNullException(nameof(entity));
|
||||
var table = Orm.CodeFirst.GetTableByEntity(EntityType);
|
||||
if (table.Primarys.Any() == false) throw new Exception(DbContextStrings.CannotAdd_EntityHasNo_PrimaryKey(Orm.GetEntityString(EntityType, entity)));
|
||||
|
||||
var flagExists = ExistsInStates(entity);
|
||||
if (flagExists == false)
|
||||
{
|
||||
var olddata = Select.WhereDynamic(entity).First();
|
||||
flagExists = olddata != null;
|
||||
}
|
||||
if (flagExists == true)
|
||||
{
|
||||
var affrows = Update(entity);
|
||||
if (affrows > 0) return entity;
|
||||
}
|
||||
if (table.Primarys.Where(a => a.Attribute.IsIdentity).Count() == table.Primarys.Length)
|
||||
{
|
||||
Orm.ClearEntityPrimaryValueWithIdentity(EntityType, entity);
|
||||
return Insert(entity);
|
||||
}
|
||||
throw new Exception(DbContextStrings.CannotAdd_PrimaryKey_NotSet(Orm.GetEntityString(EntityType, entity)));
|
||||
}
|
||||
|
||||
public virtual int Update(TEntity entity) => Update(new[] { entity });
|
||||
public virtual int Update(IEnumerable<TEntity> entitys)
|
||||
{
|
||||
var tracking = new AggregateRootTrackingChangeInfo();
|
||||
foreach(var entity in entitys)
|
||||
{
|
||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
||||
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以更新数据 {Orm.GetEntityString(EntityType, entity)}");
|
||||
AggregateRootUtils.CompareEntityValue(Orm, EntityType, state.Value, entity, null, tracking);
|
||||
}
|
||||
foreach (var entity in entitys)
|
||||
Attach(entity);
|
||||
|
||||
return SaveTrackingChange(tracking);
|
||||
}
|
||||
|
||||
public virtual int Delete(TEntity entity) => DeleteWithinBoundary(new[] { entity }, null);
|
||||
public virtual int Delete(IEnumerable<TEntity> entitys) => DeleteWithinBoundary(entitys, null);
|
||||
public virtual int Delete(Expression<Func<TEntity, bool>> predicate) => DeleteWithinBoundary(SelectAggregateRoot.Where(predicate).ToList(), null);
|
||||
public virtual List<object> DeleteCascadeByDatabase(Expression<Func<TEntity, bool>> predicate)
|
||||
{
|
||||
var deletedOutput = new List<object>();
|
||||
DeleteWithinBoundary(SelectAggregateRoot.Where(predicate).ToList(), deletedOutput);
|
||||
return deletedOutput;
|
||||
}
|
||||
int DeleteWithinBoundary(IEnumerable<TEntity> entitys, List<object> deletedOutput)
|
||||
{
|
||||
var tracking = new AggregateRootTrackingChangeInfo();
|
||||
foreach (var entity in entitys)
|
||||
{
|
||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
||||
AggregateRootUtils.CompareEntityValue(Orm, EntityType, entity, null, null, tracking);
|
||||
_states.Remove(stateKey);
|
||||
}
|
||||
var affrows = 0;
|
||||
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;
|
||||
}
|
||||
|
||||
public virtual void SaveMany(TEntity entity, string propertyName)
|
||||
{
|
||||
var tracking = new AggregateRootTrackingChangeInfo();
|
||||
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
|
||||
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以保存数据 {Orm.GetEntityString(EntityType, entity)}");
|
||||
AggregateRootUtils.CompareEntityValue(Orm, EntityType, state.Value, entity, propertyName, tracking);
|
||||
Attach(entity); //应该只存储 propertyName 内容
|
||||
SaveTrackingChange(tracking);
|
||||
}
|
||||
|
||||
|
||||
int SaveTrackingChange(AggregateRootTrackingChangeInfo tracking)
|
||||
{
|
||||
var affrows = 0;
|
||||
DisposeChildRepositorys();
|
||||
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)
|
||||
{
|
||||
var repo = GetChildRepository(il.Key);
|
||||
InsertWithinBoundaryStatic(repo, GetChildRepository, il.Value, out var affrowsOut);
|
||||
affrows += affrowsOut;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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());
|
||||
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 dl2 in dl.Value)
|
||||
{
|
||||
affrows += Orm.Update<object>().AsType(dl.Key).AsTable(_asTableRule)
|
||||
.SetSource(dl2.Value.Select(a => a.Item2).ToArray())
|
||||
.UpdateColumns(dl2.Value.First().Item4.ToArray())
|
||||
.ExecuteAffrows();
|
||||
}
|
||||
}
|
||||
DisposeChildRepositorys();
|
||||
return affrows;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user