AggregateRootRepository

This commit is contained in:
2881099 2022-09-02 14:19:48 +08:00
parent ca2df947e8
commit 2b8ec5c570
4 changed files with 380 additions and 247 deletions

View File

@ -13,79 +13,49 @@ using System.Threading.Tasks;
namespace FreeSql namespace FreeSql
{ {
public class AggregateRootRepository<TEntity> : IBaseRepository<TEntity> where TEntity : class public partial class AggregateRootRepository<TEntity> : IBaseRepository<TEntity> where TEntity : class
{ {
readonly IBaseRepository<TEntity> _repositoryPriv; readonly IBaseRepository<TEntity> _repository;
readonly Dictionary<Type, IBaseRepository<object>> _repositorys = new Dictionary<Type, IBaseRepository<object>>();
protected IBaseRepository<TEntity> MainRepository
{
get
{
_repositoryPriv.DbContextOptions.EnableCascadeSave = false;
return _repositoryPriv;
}
}
protected IBaseRepository<object> GetOrAddRepository(Type otherEntityType)
{
if (_repositorys.TryGetValue(otherEntityType, out var repo) == false)
{
repo = Orm.GetRepository<object>();
repo.AsType(otherEntityType);
_repositorys.Add(otherEntityType, repo);
}
repo.UnitOfWork = UnitOfWork;
repo.DbContextOptions = DbContextOptions;
repo.DbContextOptions.EnableCascadeSave = false;
repo.AsTable(_asTableRule);
return repo;
}
public AggregateRootRepository(IFreeSql fsql) public AggregateRootRepository(IFreeSql fsql)
{ {
_repositoryPriv = fsql.GetRepository<TEntity>(); if (fsql == null) throw new ArgumentNullException(nameof(fsql));
_repository = fsql.GetRepository<TEntity>();
_repository.DbContextOptions.EnableCascadeSave = false;
} }
public AggregateRootRepository(IFreeSql fsql, UnitOfWorkManager uowManager) public AggregateRootRepository(IFreeSql fsql, UnitOfWorkManager uowManager) : this(uowManager?.Orm ?? fsql)
{ {
_repositoryPriv = (uowManager?.Orm ?? fsql).GetRepository<TEntity>(); uowManager?.Binding(_repository);
uowManager?.Binding(_repositoryPriv);
}
protected void DisposeRepositorys()
{
foreach (var repo in _repositorys.Values)
{
repo.FlushState();
repo.Dispose();
}
_repositorys.Clear();
} }
public void Dispose() public void Dispose()
{ {
foreach (var repo in _repositorys.Values) DisposeChildRepositorys();
{ _repository.Dispose();
repo.FlushState(); FlushState();
repo.Dispose();
}
_repositorys.Clear();
_repositoryPriv.FlushState();
_repositoryPriv.Dispose();
} }
public IUnitOfWork UnitOfWork public IFreeSql Orm => _repository.Orm;
{ public IUnitOfWork UnitOfWork { get => _repository.UnitOfWork; set => _repository.UnitOfWork = value; }
get => _repositoryPriv.UnitOfWork;
set => _repositoryPriv.UnitOfWork = value;
}
public DbContextOptions DbContextOptions public DbContextOptions DbContextOptions
{ {
get => _repositoryPriv.DbContextOptions; get => _repository.DbContextOptions; set
set => _repositoryPriv.DbContextOptions = value ?? throw new ArgumentNullException(nameof(DbContextOptions)); {
if (value != null)
{
_repository.DbContextOptions = value;
_repository.DbContextOptions.EnableCascadeSave = false;
}
}
} }
public void AsType(Type entityType) => _repositoryPriv.AsType(entityType); public void AsType(Type entityType) => _repository.AsType(entityType);
Func<string, string> _asTableRule; Func<string, string> _asTableRule;
public void AsTable(Func<string, string> rule) => _repositoryPriv.AsTable(_asTableRule = rule); public void AsTable(Func<string, string> rule)
public Type EntityType => _repositoryPriv.EntityType; {
public IDataFilter<TEntity> DataFilter => _repositoryPriv.DataFilter; _repository.AsTable(rule);
_asTableRule = rule;
}
TableInfo _table;
public Type EntityType => _repository.EntityType;
public IDataFilter<TEntity> DataFilter => _repository.DataFilter;
public void Attach(TEntity entity) => AttachCascade(entity); public void Attach(TEntity entity) => AttachCascade(entity);
public void Attach(IEnumerable<TEntity> entity) public void Attach(IEnumerable<TEntity> entity)
@ -93,53 +63,44 @@ namespace FreeSql
foreach (var item in entity) foreach (var item in entity)
AttachCascade(item); AttachCascade(item);
} }
public IBaseRepository<TEntity> AttachOnlyPrimary(TEntity data) => _repositoryPriv.AttachOnlyPrimary(data); public IBaseRepository<TEntity> AttachOnlyPrimary(TEntity data) => _repository.AttachOnlyPrimary(data);
public Dictionary<string, object[]> CompareState(TEntity newdata) => _repositoryPriv.CompareState(newdata); public Dictionary<string, object[]> CompareState(TEntity newdata) => _repository.CompareState(newdata);
public void FlushState() => _repositoryPriv.FlushState(); public void FlushState()
{
_repository.FlushState();
_states.Clear();
}
public IFreeSql Orm => _repositoryPriv.Orm; public IUpdate<TEntity> UpdateDiy => _repository.UpdateDiy;
public IUpdate<TEntity> UpdateDiy => _repositoryPriv.UpdateDiy;
public ISelect<TEntity> Where(Expression<Func<TEntity, bool>> exp) => Select.Where(exp); public ISelect<TEntity> Where(Expression<Func<TEntity, bool>> exp) => Select.Where(exp);
public ISelect<TEntity> WhereIf(bool condition, Expression<Func<TEntity, bool>> exp) => Select.WhereIf(condition, exp); public ISelect<TEntity> WhereIf(bool condition, Expression<Func<TEntity, bool>> exp) => Select.WhereIf(condition, exp);
public TEntity Insert(TEntity entity) => Insert(new[] { entity }).FirstOrDefault(); readonly Dictionary<Type, IBaseRepository<object>> _childRepositorys = new Dictionary<Type, IBaseRepository<object>>();
public List<TEntity> Insert(IEnumerable<TEntity> entitys) IBaseRepository<object> GetChildRepository(Type type)
{ {
var ret = InsertCascade(MainRepository, GetOrAddRepository, entitys, new Dictionary<string, object>()); if (_childRepositorys.TryGetValue(type, out var repo) == false)
DisposeRepositorys(); {
foreach (var item in ret) repo = Orm.GetRepository<object>();
AttachCascade(item); repo.AsType(type);
return ret; _childRepositorys.Add(type, repo);
}
repo.UnitOfWork = UnitOfWork;
repo.DbContextOptions = DbContextOptions;
repo.DbContextOptions.EnableCascadeSave = false;
repo.AsTable(_asTableRule);
return repo;
}
void DisposeChildRepositorys()
{
foreach (var repo in _childRepositorys.Values)
{
repo.FlushState();
repo.Dispose();
}
_childRepositorys.Clear();
} }
public int Update(TEntity entity) => Update(new[] { entity }); #region State
public int Update(IEnumerable<TEntity> entitys)
{
var ret = UpdateCascade(MainRepository, GetOrAddRepository, entitys, new Dictionary<string, object>());
DisposeRepositorys();
foreach (var item in entitys)
AttachCascade(item);
return ret;
}
public int Delete(TEntity entity) => Delete(new[] { entity });
#if net40
#else
public Task<TEntity> InsertAsync(TEntity entity, CancellationToken cancellationToken = default) => _repositoryPriv.InsertAsync(entity, cancellationToken);
public Task<List<TEntity>> InsertAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => _repositoryPriv.InsertAsync(entitys, cancellationToken);
public Task<TEntity> InsertOrUpdateAsync(TEntity entity, CancellationToken cancellationToken = default) => _repositoryPriv.InsertOrUpdateAsync(entity, cancellationToken);
public Task SaveManyAsync(TEntity entity, string propertyName, CancellationToken cancellationToken = default) => _repositoryPriv.SaveManyAsync(entity, propertyName, cancellationToken);
public Task<int> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default) => _repositoryPriv.UpdateAsync(entity, cancellationToken);
public Task<int> UpdateAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => _repositoryPriv.UpdateAsync(entitys, cancellationToken);
public Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default) => _repositoryPriv.DeleteAsync(entity, cancellationToken);
public Task<int> DeleteAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => _repositoryPriv.DeleteAsync(entitys, cancellationToken);
public Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => _repositoryPriv.DeleteAsync(predicate, cancellationToken);
public Task<List<object>> DeleteCascadeByDatabaseAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => _repositoryPriv.DeleteCascadeByDatabaseAsync(predicate, cancellationToken);
#endif
protected Dictionary<string, EntityState> _states = new Dictionary<string, EntityState>(); protected Dictionary<string, EntityState> _states = new Dictionary<string, EntityState>();
protected class EntityState protected class EntityState
{ {
@ -175,13 +136,14 @@ namespace FreeSql
if (_states.ContainsKey(state.Key)) _states[state.Key] = state; if (_states.ContainsKey(state.Key)) _states[state.Key] = state;
else _states.Add(state.Key, state); else _states.Add(state.Key, state);
} }
#endregion
#region Select #region Select
public ISelect<TEntity> Select public virtual ISelect<TEntity> Select
{ {
get get
{ {
var query = MainRepository.Select.TrackToList(SelectTrackingAggregateRootNavigate); var query = _repository.Select.TrackToList(SelectTrackingAggregateRootNavigate);
SelectFetchAggregateRootNavigate(query, EntityType, "", new Stack<Type>()); SelectFetchAggregateRootNavigate(query, EntityType, "", new Stack<Type>());
return query; return query;
} }
@ -190,10 +152,10 @@ namespace FreeSql
{ {
if (ignores.Any(a => a == entityType)) return; if (ignores.Any(a => a == entityType)) return;
ignores.Push(entityType); ignores.Push(entityType);
var tb = Orm.CodeFirst.GetTableByEntity(entityType); var table = Orm.CodeFirst.GetTableByEntity(entityType);
foreach (var prop in tb.Properties.Values) foreach (var prop in table.Properties.Values)
{ {
var tbref = tb.GetTableRef(prop.Name, false); var tbref = table.GetTableRef(prop.Name, false);
if (tbref == null) continue; if (tbref == null) continue;
if (!string.IsNullOrWhiteSpace(navigatePath)) navigatePath = $"{navigatePath}."; if (!string.IsNullOrWhiteSpace(navigatePath)) navigatePath = $"{navigatePath}.";
var navigateExpression = $"{navigatePath}{prop.Name}"; var navigateExpression = $"{navigatePath}{prop.Name}";
@ -247,124 +209,5 @@ namespace FreeSql
} }
#endregion #endregion
protected static List<T1> InsertCascade<T1>(IBaseRepository<T1> repository, Func<Type, IBaseRepository<object>> getOrAddRepository, IEnumerable<T1> entitys, Dictionary<string, object> states) where T1 : class
{
var ret = repository.Insert(entitys);
foreach (var entity in entitys) IsCascade(repository.EntityType, entity, true);
var table = repository.Orm.CodeFirst.GetTableByEntity(repository.EntityType);
foreach (var prop in table.Properties.Values)
{
var tbref = table.GetTableRef(prop.Name, false);
if (tbref == null) continue;
switch (tbref.RefType)
{
case TableRefType.OneToOne:
var otoList = ret.Select(entity =>
{
var otoItem = table.GetPropertyValue(entity, prop.Name);
if (IsCascade(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 = getOrAddRepository(tbref.RefEntityType);
InsertCascade(repo, getOrAddRepository, otoList, states);
}
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 (IsCascade(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 = getOrAddRepository(tbref.RefEntityType);
InsertCascade(repo, getOrAddRepository, otmList, states);
}
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 = getOrAddRepository(tbref.RefMiddleEntityType);
InsertCascade(repo, getOrAddRepository, mtmMidList, states);
}
break;
case TableRefType.PgArrayToMany:
break;
}
}
return ret;
bool IsCascade(Type entityType, object entity, bool isadd)
{
var stateKey = repository.Orm.GetEntityKeyString(entityType, entity, false);
if (stateKey == null) return true;
stateKey = $"{stateKey}*|_,[,_|*{entityType.DisplayCsharp()}";
if (states.ContainsKey(stateKey)) return false;
if (isadd) states.Add(stateKey, entity);
return true;
}
}
public TEntity InsertOrUpdate(TEntity entity) => MainRepository.InsertOrUpdate(entity);
public int UpdateCascade<T1>(IBaseRepository<T1> repository, Func<Type, IBaseRepository<object>> getOrAddRepository, IEnumerable<T1> entitys, Dictionary<string, object> states) where T1 : class
{
return 0;
}
public int Delete(IEnumerable<TEntity> entitys) => MainRepository.Delete(entitys);
public int Delete(Expression<Func<TEntity, bool>> predicate) => MainRepository.Delete(predicate);
public List<object> DeleteCascadeByDatabase(Expression<Func<TEntity, bool>> predicate) => MainRepository.DeleteCascadeByDatabase(predicate);
public void SaveMany(TEntity entity, string propertyName) => MainRepository.SaveMany(entity, propertyName);
public void BeginEdit(List<TEntity> data) => MainRepository.BeginEdit(data);
public int EndEdit(List<TEntity> data = null) => MainRepository.EndEdit(data);
protected static void FetchAggregateRootNavigate<T1>(IFreeSql orm, Type entityType, Func<Type, TableRef, T1> callback, Dictionary<Type, bool> ignores)
{
if (ignores.ContainsKey(entityType)) return;
ignores.Add(entityType, true);
var tb = orm.CodeFirst.GetTableByEntity(entityType);
foreach (var prop in tb.Properties.Values)
{
var tbref = tb.GetTableRef(prop.Name, false);
if (tbref == null) continue;
switch (tbref.RefType)
{
case TableRefType.OneToOne:
callback(tb.Type, tbref);
FetchAggregateRootNavigate(orm, tbref.RefEntityType, callback, ignores);
break;
case TableRefType.OneToMany:
callback(tb.Type, tbref);
FetchAggregateRootNavigate(orm, tbref.RefEntityType, callback, ignores);
break;
case TableRefType.ManyToMany:
callback(tb.Type, tbref);
FetchAggregateRootNavigate(orm, tbref.RefMiddleEntityType, callback, ignores);
break;
case TableRefType.PgArrayToMany:
break;
}
}
}
} }
} }

View File

@ -0,0 +1,35 @@
#if net40
#else
using FreeSql.Extensions.EntityUtil;
using FreeSql.Internal;
using FreeSql.Internal.Model;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FreeSql
{
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> InsertOrUpdateAsync(TEntity entity, CancellationToken cancellationToken = default) => _repository.InsertOrUpdateAsync(entity, cancellationToken);
public Task SaveManyAsync(TEntity entity, string propertyName, CancellationToken cancellationToken = default) => _repository.SaveManyAsync(entity, propertyName, cancellationToken);
public Task<int> UpdateAsync(TEntity entity, CancellationToken cancellationToken = default) => _repository.UpdateAsync(entity, cancellationToken);
public Task<int> UpdateAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => _repository.UpdateAsync(entitys, cancellationToken);
public Task<int> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default) => _repository.DeleteAsync(entity, cancellationToken);
public Task<int> DeleteAsync(IEnumerable<TEntity> entitys, CancellationToken cancellationToken = default) => _repository.DeleteAsync(entitys, cancellationToken);
public Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => _repository.DeleteAsync(predicate, cancellationToken);
public Task<List<object>> DeleteCascadeByDatabaseAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => _repository.DeleteCascadeByDatabaseAsync(predicate, cancellationToken);
}
}
#endif

View File

@ -0,0 +1,257 @@
using FreeSql.Extensions.EntityUtil;
using FreeSql.Internal;
using FreeSql.Internal.Model;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace FreeSql
{
partial class AggregateRootRepository<TEntity>
{
public TEntity Insert(TEntity entity) => InsertCascade(new[] { entity }).FirstOrDefault();
public List<TEntity> Insert(IEnumerable<TEntity> entitys) => InsertCascade(entitys);
public TEntity InsertOrUpdate(TEntity entity) => InsertOrUpdateCascade(entity);
public int Update(TEntity entity) => UpdateCascade(new[] { entity });
public int Update(IEnumerable<TEntity> entitys) => UpdateCascade(entitys);
public int Delete(TEntity entity) => DeleteCascade(new[] { entity });
public int Delete(IEnumerable<TEntity> entitys) => DeleteCascade(entitys);
public int Delete(Expression<Func<TEntity, bool>> predicate) => DeleteCascade(Where(predicate).ToList());
public List<object> DeleteCascadeByDatabase(Expression<Func<TEntity, bool>> predicate)
{
var deletedOutput = new List<object>();
DeleteCascade(Where(predicate).ToList(), deletedOutput);
return deletedOutput;
}
public void SaveMany(TEntity entity, string propertyName) => SaveManyCascade(entity, propertyName);
protected virtual List<TEntity> InsertCascade(IEnumerable<TEntity> entitys)
{
var repos = new Dictionary<Type, object>();
try
{
return InsertCascadeStatic(_repository, GetChildRepository, entitys);
}
finally
{
DisposeChildRepositorys();
_repository.FlushState();
}
}
protected static List<TEntity> InsertCascadeStatic(IBaseRepository<TEntity> rootRepository, Func<Type, IBaseRepository<object>> getChildRepository, IEnumerable<TEntity> rootEntitys) {
Dictionary<Type, Dictionary<string, bool>> ignores = new Dictionary<Type, Dictionary<string, bool>>();
Dictionary<Type, IBaseRepository<object>> repos = new Dictionary<Type, IBaseRepository<object>>();
return LocalInsertCascade(rootRepository, rootEntitys);
bool LocalCanCascade(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<T1> LocalInsertCascade<T1>(IBaseRepository<T1> repository, IEnumerable<T1> entitys) where T1 : class
{
var ret = repository.Insert(entitys);
foreach (var entity in entitys) LocalCanCascade(repository.EntityType, entity, true);
var table = repository.Orm.CodeFirst.GetTableByEntity(repository.EntityType);
foreach (var prop in table.Properties.Values)
{
var tbref = table.GetTableRef(prop.Name, false);
if (tbref == null) continue;
switch (tbref.RefType)
{
case TableRefType.OneToOne:
var otoList = ret.Select(entity =>
{
var otoItem = table.GetPropertyValue(entity, prop.Name);
if (LocalCanCascade(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);
LocalInsertCascade(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 (LocalCanCascade(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);
LocalInsertCascade(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);
LocalInsertCascade(repo, mtmMidList);
}
break;
case TableRefType.PgArrayToMany:
break;
}
}
return ret;
}
}
protected virtual TEntity InsertOrUpdateCascade(TEntity entity)
{
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
if (entity == null) throw new ArgumentNullException(nameof(entity));
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 = UpdateCascade(new[] { entity });
if (affrows > 0) return entity;
}
Orm.ClearEntityPrimaryValueWithIdentity(EntityType, entity);
return InsertCascade(new[] { entity }).FirstOrDefault();
}
protected virtual int UpdateCascade(IEnumerable<TEntity> entitys)
{
List<NativeTuple<Type, object>> insertLog = new List<NativeTuple<Type, object>>();
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)
{
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以更新数据 {Orm.GetEntityString(EntityType, entity)}");
AggregateRootUtils.CompareEntityValueCascade(Orm, EntityType, state.Value, entity, null, insertLog, updateLog, deleteLog);
AttachCascade(entity);
}
return insertLog.Count + updateLog.Count + deleteLog.Count;
}
protected virtual int DeleteCascade(IEnumerable<TEntity> entitys, List<object> deletedOutput = null)
{
List<NativeTuple<Type, object>> insertLog = new List<NativeTuple<Type, object>>();
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)
{
var stateKey = Orm.GetEntityKeyString(EntityType, entity, false);
AggregateRootUtils.CompareEntityValueCascade(Orm, EntityType, entity, null, null, insertLog, updateLog, deleteLog);
_states.Remove(stateKey);
}
if (deletedOutput != null) deletedOutput.AddRange(deleteLog.Select(a => a.Item2));
return deleteLog.Count;
}
protected virtual void SaveManyCascade(TEntity entity, string propertyName)
{
List<NativeTuple<Type, object>> insertLog = new List<NativeTuple<Type, object>>();
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);
if (_states.TryGetValue(stateKey, out var state) == false) throw new Exception($"AggregateRootRepository 使用仓储对象查询后,才可以保存数据 {Orm.GetEntityString(EntityType, entity)}");
AggregateRootUtils.CompareEntityValueCascade(Orm, EntityType, state.Value, entity, propertyName, insertLog, updateLog, deleteLog);
AttachCascade(entity);
}
protected List<TEntity> _dataEditing;
protected ConcurrentDictionary<string, EntityState> _statesEditing = new ConcurrentDictionary<string, EntityState>();
public void BeginEdit(List<TEntity> data)
{
if (data == null) return;
var table = Orm.CodeFirst.GetTableByEntity(EntityType);
if (table.Primarys.Any() == false) throw new Exception(DbContextStrings.CannotEdit_EntityHasNo_PrimaryKey(Orm.GetEntityString(EntityType, data.First())));
_statesEditing.Clear();
_dataEditing = data;
foreach (var item in data)
{
var key = Orm.GetEntityKeyString(EntityType, item, false);
if (string.IsNullOrEmpty(key)) continue;
_statesEditing.AddOrUpdate(key, k => CreateEntityState(item), (k, ov) =>
{
AggregateRootUtils.MapEntityValueCascade(Orm, EntityType, item, ov.Value);
ov.Time = DateTime.Now;
return ov;
});
}
}
public int EndEdit(List<TEntity> data = null)
{
if (data == null) data = _dataEditing;
if (data == null) return 0;
List<NativeTuple<Type, object>> insertLog = new List<NativeTuple<Type, object>>();
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
{
var addList = new List<TEntity>();
var ediList = new List<TEntity>();
foreach (var item in data)
{
var key = Orm.GetEntityKeyString(EntityType, item, false);
if (_statesEditing.TryRemove(key, out var state) == false)
{
insertLog.Add(NativeTuple.Create(EntityType, (object)item));
continue;
}
_states[key] = state;
AggregateRootUtils.CompareEntityValueCascade(Orm, EntityType, state.Value, item, null, insertLog, updateLog, deleteLog);
}
foreach (var item in _statesEditing.Values.OrderBy(a => a.Time))
{
AggregateRootUtils.CompareEntityValueCascade(Orm, EntityType, item, null, null, insertLog, updateLog, deleteLog);
}
}
finally
{
_dataEditing = null;
_statesEditing.Clear();
}
return insertLog.Count + updateLog.Count + deleteLog.Count;
}
}
}

View File

@ -1,4 +1,4 @@
using FreeSql.Aop; using FreeSql;
using FreeSql.Extensions.EntityUtil; using FreeSql.Extensions.EntityUtil;
using FreeSql.Internal; using FreeSql.Internal;
using FreeSql.Internal.Model; using FreeSql.Internal.Model;
@ -8,20 +8,32 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Text;
static class AggregateRootUtils static class AggregateRootUtils
{ {
public static void CompareEntityValueCascade(IFreeSql fsql, Type entityType, object entityBefore, object entityAfter, string navigatePropertyName,
public static void CompareEntityValueCascade(IFreeSql fsql, Type entityType, object entityBefore, object entityAfter, List<NativeTuple<Type, object>> insertLog,
List<NativeTuple<Type, object>> insertLog,
List<NativeTuple<Type, object, object, List<string>>> updateLog, List<NativeTuple<Type, object, object, List<string>>> updateLog,
List<NativeTuple<Type, object>> deleteLog) List<NativeTuple<Type, object>> deleteLog)
{ {
if (entityType == null) entityType = entityBefore?.GetType() ?? entityAfter?.GetType(); if (entityType == null) entityType = entityBefore?.GetType() ?? entityAfter?.GetType();
var table = fsql.CodeFirst.GetTableByEntity(entityType); var table = fsql.CodeFirst.GetTableByEntity(entityType);
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, entityBefore));
EachNavigateCascade(fsql, entityType, entityBefore, (path, tr, ct, stackvs) =>
{
deleteLog.Add(NativeTuple.Create(ct, stackvs.First()));
});
return;
}
var changes = new List<string>(); var changes = new List<string>();
foreach (var col in table.ColumnsByCs.Values) foreach (var col in table.ColumnsByCs.Values)
{ {
@ -42,27 +54,13 @@ static class AggregateRootUtils
{ {
var tbref = table.GetTableRef(prop.Name, false); var tbref = table.GetTableRef(prop.Name, false);
if (tbref == null) continue; if (tbref == null) continue;
if (navigatePropertyName != null && prop.Name != navigatePropertyName) continue;
var propvalBefore = table.GetPropertyValue(entityBefore, prop.Name); var propvalBefore = table.GetPropertyValue(entityBefore, prop.Name);
var propvalAfter = table.GetPropertyValue(entityBefore, prop.Name); var propvalAfter = table.GetPropertyValue(entityBefore, prop.Name);
switch (tbref.RefType) switch (tbref.RefType)
{ {
case TableRefType.OneToOne: case TableRefType.OneToOne:
if (propvalBefore == null && propvalAfter == null) return; CompareEntityValueCascade(fsql, tbref.RefEntityType, propvalBefore, propvalAfter, null, insertLog, updateLog, deleteLog);
if (propvalBefore == null && propvalAfter != null)
{
insertLog.Add(NativeTuple.Create(tbref.RefEntityType, propvalAfter));
return;
}
if (propvalBefore != null && propvalAfter == null)
{
deleteLog.Add(NativeTuple.Create(tbref.RefEntityType, propvalBefore));
EachNavigateCascade(fsql, tbref.RefEntityType, propvalBefore, (path, tr, ct, stackvs) =>
{
deleteLog.Add(NativeTuple.Create(ct, stackvs.First()));
});
return;
}
CompareEntityValueCascade(fsql, tbref.RefEntityType, propvalBefore, propvalAfter, insertLog, updateLog, deleteLog);
break; break;
case TableRefType.OneToMany: case TableRefType.OneToMany:
LocalCompareEntityValueCollection(tbref, propvalBefore as IEnumerable, propvalAfter as IEnumerable); LocalCompareEntityValueCollection(tbref, propvalBefore as IEnumerable, propvalAfter as IEnumerable);
@ -84,7 +82,7 @@ static class AggregateRootUtils
if (collectionBefore == null && collectionAfter == null) return; if (collectionBefore == null && collectionAfter == null) return;
if (collectionBefore == null && collectionAfter != null) if (collectionBefore == null && collectionAfter != null)
{ {
foreach(var item in collectionAfter) foreach (var item in collectionAfter)
insertLog.Add(NativeTuple.Create(elementType, item)); insertLog.Add(NativeTuple.Create(elementType, item));
return; return;
} }
@ -135,7 +133,7 @@ static class AggregateRootUtils
} }
} }
foreach (var key in dictBefore.Keys) foreach (var key in dictBefore.Keys)
CompareEntityValueCascade(fsql, elementType, dictBefore[key], dictAfter[key], insertLog, updateLog, deleteLog); CompareEntityValueCascade(fsql, elementType, dictBefore[key], dictAfter[key], null, insertLog, updateLog, deleteLog);
} }
} }
public static void EachNavigateCascade(IFreeSql fsql, Type rootType, object rootEntity, Action<string, TableRef, Type, List<object>> callback) public static void EachNavigateCascade(IFreeSql fsql, Type rootType, object rootEntity, Action<string, TableRef, Type, List<object>> callback)