Skip to content

Commit 0ecab32

Browse files
committed
- 调整 DbSet/Repository DeleteCascadeByDatabase/Delete 区别于数据库/内存的级联删除;#609
1 parent a80d2cd commit 0ecab32

File tree

40 files changed

+422
-357
lines changed

40 files changed

+422
-357
lines changed

FreeSql.DbContext/DbContext/DbContext.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public DbContextOptions Options
4646
_optionsPriv = new DbContextOptions();
4747
if (FreeSqlDbContextExtensions._dicSetDbContextOptions.TryGetValue(OrmOriginal.Ado.Identifier, out var opt))
4848
{
49-
_optionsPriv.EnableAddOrUpdateNavigate = opt.EnableAddOrUpdateNavigate;
49+
_optionsPriv.EnableCascadeSave = opt.EnableCascadeSave;
5050
_optionsPriv.EnableGlobalFilter = opt.EnableGlobalFilter;
5151
_optionsPriv.NoneParameter = opt.NoneParameter;
5252
_optionsPriv.OnEntityChange = opt.OnEntityChange;
@@ -225,29 +225,29 @@ public Dictionary<string, object[]> CompareState<TEntity>(TEntity newdata) where
225225
}
226226
#if net40
227227
#else
228-
public Task AddAsync<TEntity>(TEntity data) where TEntity : class
228+
public Task AddAsync<TEntity>(TEntity data, CancellationToken cancellationToken = default) where TEntity : class
229229
{
230230
CheckEntityTypeOrThrow(typeof(TEntity));
231-
return this.Set<TEntity>().AddAsync(data);
231+
return this.Set<TEntity>().AddAsync(data, cancellationToken);
232232
}
233-
public Task AddRangeAsync<TEntity>(IEnumerable<TEntity> data) where TEntity : class => this.Set<TEntity>().AddRangeAsync(data);
233+
public Task AddRangeAsync<TEntity>(IEnumerable<TEntity> data, CancellationToken cancellationToken = default) where TEntity : class => this.Set<TEntity>().AddRangeAsync(data, cancellationToken);
234234

235-
public Task UpdateAsync<TEntity>(TEntity data) where TEntity : class
235+
public Task UpdateAsync<TEntity>(TEntity data, CancellationToken cancellationToken = default) where TEntity : class
236236
{
237237
CheckEntityTypeOrThrow(typeof(TEntity));
238-
return this.Set<TEntity>().UpdateAsync(data);
238+
return this.Set<TEntity>().UpdateAsync(data, cancellationToken);
239239
}
240-
public Task UpdateRangeAsync<TEntity>(IEnumerable<TEntity> data) where TEntity : class => this.Set<TEntity>().UpdateRangeAsync(data);
240+
public Task UpdateRangeAsync<TEntity>(IEnumerable<TEntity> data, CancellationToken cancellationToken = default) where TEntity : class => this.Set<TEntity>().UpdateRangeAsync(data, cancellationToken);
241241

242-
public Task AddOrUpdateAsync<TEntity>(TEntity data) where TEntity : class
242+
public Task AddOrUpdateAsync<TEntity>(TEntity data, CancellationToken cancellationToken = default) where TEntity : class
243243
{
244244
CheckEntityTypeOrThrow(typeof(TEntity));
245-
return this.Set<TEntity>().AddOrUpdateAsync(data);
245+
return this.Set<TEntity>().AddOrUpdateAsync(data, cancellationToken);
246246
}
247-
public Task SaveManyAsync<TEntity>(TEntity data, string propertyName) where TEntity : class
247+
public Task SaveManyAsync<TEntity>(TEntity data, string propertyName, CancellationToken cancellationToken = default) where TEntity : class
248248
{
249249
CheckEntityTypeOrThrow(typeof(TEntity));
250-
return this.Set<TEntity>().SaveManyAsync(data, propertyName);
250+
return this.Set<TEntity>().SaveManyAsync(data, propertyName, cancellationToken);
251251
}
252252
#endif
253253
#endregion

FreeSql.DbContext/DbContext/DbContextOptions.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,16 @@ public class DbContextOptions
2121
/// - 属性集合为空时(!=null),删除他们的所有关联数据(中间表)<para></para>
2222
/// - 属性集合不为空时,与数据库存在的关联数据(中间表)完全对比,计算出应该删除和添加的记录
2323
/// </summary>
24-
public bool EnableAddOrUpdateNavigate { get; set; } = false;
24+
public bool EnableCascadeSave { get; set; } = false;
2525

2626
/// <summary>
27-
/// 因增加支持 OneToOne 级联保存,请了解机制,已改名为 EnableAddOrUpdateNavigate
27+
/// 因增加支持 OneToOne 级联保存,和基于内存的级联删除,已改名为 EnableCascadeSave
2828
/// </summary>
29-
[Obsolete("因增加支持 OneToOne 级联保存,请了解机制,已改名为 EnableAddOrUpdateNavigate")]
29+
[Obsolete("因增加支持 OneToOne 级联保存,和基于内存的级联删除,已改名为 EnableCascadeSave")]
3030
public bool EnableAddOrUpdateNavigateList
3131
{
32-
get => EnableAddOrUpdateNavigate;
33-
set => EnableAddOrUpdateNavigate = value;
32+
get => EnableCascadeSave;
33+
set => EnableCascadeSave = value;
3434
}
3535

3636
/// <summary>

FreeSql.DbContext/DbSet/DbSet.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,15 @@ void StatesCopyToDbSetObject(DbSet<object> ds)
166166
{
167167
ds.AttachRange(_states.Values.OrderBy(a => a.Time).Select(a => a.Value).ToArray());
168168
}
169+
void StatesRemoveByObjects(IEnumerable<object> data)
170+
{
171+
if (data == null) return;
172+
foreach (var item in data)
173+
{
174+
var stateKey = _db.OrmOriginal.GetEntityKeyString(_entityType, item, false);
175+
_states.TryRemove(stateKey, out var trystate);
176+
}
177+
}
169178

170179
public class EntityState
171180
{

FreeSql.DbContext/DbSet/DbSetAsync.cs

Lines changed: 14 additions & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ async Task AddPrivAsync(TEntity data, bool isCheck, CancellationToken cancellati
5555
_db.OrmOriginal.SetEntityValueWithPropertyName(_entityType, data, _tableIdentitys[0].CsName, idtval);
5656
_db._entityChangeReport.Add(new DbContext.EntityChangeReport.ChangeInfo { Object = data, Type = DbContext.EntityChangeType.Insert });
5757
Attach(data);
58-
if (_db.Options.EnableAddOrUpdateNavigate)
58+
if (_db.Options.EnableCascadeSave)
5959
await AddOrUpdateNavigateAsync(data, true, null, cancellationToken);
6060
}
6161
else
@@ -66,7 +66,7 @@ async Task AddPrivAsync(TEntity data, bool isCheck, CancellationToken cancellati
6666
IncrAffrows(1);
6767
_db.OrmOriginal.MapEntityValue(_entityType, newval, data);
6868
Attach(newval);
69-
if (_db.Options.EnableAddOrUpdateNavigate)
69+
if (_db.Options.EnableCascadeSave)
7070
await AddOrUpdateNavigateAsync(data, true, null, cancellationToken);
7171
}
7272
return;
@@ -79,7 +79,7 @@ async Task AddPrivAsync(TEntity data, bool isCheck, CancellationToken cancellati
7979
_db.OrmOriginal.SetEntityValueWithPropertyName(_entityType, data, _tableIdentitys[0].CsName, idtval);
8080
_db._entityChangeReport.Add(new DbContext.EntityChangeReport.ChangeInfo { Object = data, Type = DbContext.EntityChangeType.Insert });
8181
Attach(data);
82-
if (_db.Options.EnableAddOrUpdateNavigate)
82+
if (_db.Options.EnableCascadeSave)
8383
await AddOrUpdateNavigateAsync(data, true, null, cancellationToken);
8484
return;
8585
}
@@ -88,7 +88,7 @@ async Task AddPrivAsync(TEntity data, bool isCheck, CancellationToken cancellati
8888
}
8989
EnqueueToDbContext(DbContext.EntityChangeType.Insert, CreateEntityState(data));
9090
Attach(data);
91-
if (_db.Options.EnableAddOrUpdateNavigate)
91+
if (_db.Options.EnableCascadeSave)
9292
await AddOrUpdateNavigateAsync(data, true, null, cancellationToken);
9393
}
9494
public Task AddAsync(TEntity data, CancellationToken cancellationToken = default) => AddPrivAsync(data, true, cancellationToken);
@@ -121,7 +121,7 @@ async public Task AddRangeAsync(IEnumerable<TEntity> data, CancellationToken can
121121
_db.OrmOriginal.MapEntityValue(_entityType, rets[idx++], s);
122122
IncrAffrows(rets.Count);
123123
AttachRange(rets);
124-
if (_db.Options.EnableAddOrUpdateNavigate)
124+
if (_db.Options.EnableCascadeSave)
125125
foreach (var item in data)
126126
await AddOrUpdateNavigateAsync(item, true, null, cancellationToken);
127127
return;
@@ -139,7 +139,7 @@ async public Task AddRangeAsync(IEnumerable<TEntity> data, CancellationToken can
139139
foreach (var item in data)
140140
EnqueueToDbContext(DbContext.EntityChangeType.Insert, CreateEntityState(item));
141141
AttachRange(data);
142-
if (_db.Options.EnableAddOrUpdateNavigate)
142+
if (_db.Options.EnableCascadeSave)
143143
foreach (var item in data)
144144
await AddOrUpdateNavigateAsync(item, true, null, cancellationToken);
145145
}
@@ -161,8 +161,8 @@ async public Task SaveManyAsync(TEntity item, string propertyName, CancellationT
161161
}
162162

163163
await DbContextFlushCommandAsync(cancellationToken);
164-
var oldEnable = _db.Options.EnableAddOrUpdateNavigate;
165-
_db.Options.EnableAddOrUpdateNavigate = false;
164+
var oldEnable = _db.Options.EnableCascadeSave;
165+
_db.Options.EnableCascadeSave = false;
166166
try
167167
{
168168
await AddOrUpdateNavigateAsync(item, false, propertyName, cancellationToken);
@@ -198,7 +198,7 @@ async public Task SaveManyAsync(TEntity item, string propertyName, CancellationT
198198
}
199199
finally
200200
{
201-
_db.Options.EnableAddOrUpdateNavigate = oldEnable;
201+
_db.Options.EnableCascadeSave = oldEnable;
202202
}
203203
}
204204
async Task AddOrUpdateNavigateAsync(TEntity item, bool isAdd, string propertyName, CancellationToken cancellationToken)
@@ -447,7 +447,7 @@ async Task UpdateRangePrivAsync(IEnumerable<TEntity> data, bool isCheck, Cancell
447447
state.OldValue = item;
448448
EnqueueToDbContext(DbContext.EntityChangeType.Update, state);
449449
}
450-
if (_db.Options.EnableAddOrUpdateNavigate)
450+
if (_db.Options.EnableCascadeSave)
451451
foreach (var item in data)
452452
await AddOrUpdateNavigateAsync(item, false, null, cancellationToken);
453453
}
@@ -499,155 +499,11 @@ async public Task AddOrUpdateAsync(TEntity data, CancellationToken cancellationT
499499
#endregion
500500

501501
#region RemoveCascadeAsync
502-
public Task<List<object>> RemoveCascadeAsync(TEntity data, CancellationToken cancellationToken = default) => RemoveRangeCascadeAsync(new[] { data }, cancellationToken);
503-
public Task<List<object>> RemoveCascadeAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => RemoveRangeCascadeAsync(Select.Where(predicate).ToList(), cancellationToken);
504-
async public Task<List<object>> RemoveRangeCascadeAsync(IEnumerable<TEntity> data, CancellationToken cancellationToken = default)
502+
public Task<List<object>> RemoveCascadeByDatabaseAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default) => RemoveRangeCascadeByMemoryOrDatabaseAsync(Select.Where(predicate).ToList(), false, cancellationToken);
503+
async internal protected Task<List<object>> RemoveRangeCascadeByMemoryOrDatabaseAsync(IEnumerable<TEntity> data, bool inMemory, CancellationToken cancellationToken = default)
505504
{
506-
var returnDeleted = new List<object>();
507-
if (data?.Any() != true) return returnDeleted;
508-
await DbContextFlushCommandAsync(cancellationToken);
509-
var fsql = _db.Orm;
510-
if (LocalGetNavigates(_table).Any() == false)
511-
{
512-
if (CanRemove(data, true) == false) return returnDeleted;
513-
foreach (var item in data) //防止清除 Identity/Guid
514-
{
515-
var state = CreateEntityState(item);
516-
_states.TryRemove(state.Key, out var trystate);
517-
518-
EnqueueToDbContext(DbContext.EntityChangeType.Delete, state);
519-
}
520-
await DbContextFlushCommandAsync(cancellationToken);
521-
returnDeleted.AddRange(data.Select(a => (object)a));
522-
return returnDeleted;
523-
}
524-
525-
var commonUtils = (fsql.Select<object>() as Internal.CommonProvider.Select0Provider)._commonUtils;
526-
var eachdic = new Dictionary<string, bool>();
527-
var rootItems = data.Select(a => (object)a).ToArray();
528-
var rootDbSet = _db.Set<object>();
529-
rootDbSet.AsType(_table.Type);
530-
rootDbSet.AttachRange(rootItems);
531-
await LocalEachAsync(rootDbSet, rootItems, true);
532-
return returnDeleted;
533-
534-
List<NativeTuple<TableRef, PropertyInfo>> LocalGetNavigates(TableInfo tb)
535-
{
536-
return tb.Properties.Where(a => tb.ColumnsByCs.ContainsKey(a.Key) == false)
537-
.Select(a => new NativeTuple<TableRef, PropertyInfo>(tb.GetTableRef(a.Key, false), a.Value))
538-
.Where(a => a.Item1 != null && a.Item1.RefType != TableRefType.ManyToOne)
539-
.ToList();
540-
}
541-
async Task LocalEachAsync(DbSet<object> dbset, IEnumerable<object> items, bool isOneToOne)
542-
{
543-
items = items?.Where(item =>
544-
{
545-
var itemkeyStr = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetEntityKeyString(fsql, dbset.EntityType, item, false);
546-
var eachdicKey = $"{dbset.EntityType.FullName},{itemkeyStr}";
547-
if (eachdic.ContainsKey(eachdicKey)) return false;
548-
eachdic.Add(eachdicKey, true);
549-
return true;
550-
}).ToList();
551-
if (items?.Any() != true) return;
552-
553-
var tb = fsql.CodeFirst.GetTableByEntity(dbset.EntityType);
554-
var navs = LocalGetNavigates(tb);
555-
556-
var otos = navs.Where(a => a.Item1.RefType == TableRefType.OneToOne).ToList();
557-
if (isOneToOne && otos.Any())
558-
{
559-
foreach (var oto in otos)
560-
{
561-
var childTable = fsql.CodeFirst.GetTableByEntity(oto.Item1.RefEntityType);
562-
var childDbSet = _db.Set<object>();
563-
childDbSet.AsType(oto.Item1.RefEntityType);
564-
var refitems = items.Select(item =>
565-
{
566-
var refitem = oto.Item1.RefEntityType.CreateInstanceGetDefaultValue();
567-
for (var a = 0; a < oto.Item1.Columns.Count; a++)
568-
{
569-
var colval = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, oto.Item1.Columns[a].CsName);
570-
FreeSql.Extensions.EntityUtil.EntityUtilExtensions.SetPropertyValue(childTable, refitem, oto.Item1.RefColumns[a].CsName, colval);
571-
}
572-
return refitem;
573-
}).ToList();
574-
var childs = await childDbSet.Select.Where(commonUtils.WhereItems(oto.Item1.RefColumns.ToArray(), "a.", refitems)).ToListAsync(false, cancellationToken);
575-
await LocalEachAsync(childDbSet, childs, false);
576-
}
577-
}
578-
579-
var otms = navs.Where(a => a.Item1.RefType == TableRefType.OneToMany).ToList();
580-
if (otms.Any())
581-
{
582-
foreach (var otm in otms)
583-
{
584-
var childTable = fsql.CodeFirst.GetTableByEntity(otm.Item1.RefEntityType);
585-
var childDbSet = _db.Set<object>();
586-
childDbSet.AsType(otm.Item1.RefEntityType);
587-
var refitems = items.Select(item =>
588-
{
589-
var refitem = otm.Item1.RefEntityType.CreateInstanceGetDefaultValue();
590-
for (var a = 0; a < otm.Item1.Columns.Count; a++)
591-
{
592-
var colval = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, otm.Item1.Columns[a].CsName);
593-
FreeSql.Extensions.EntityUtil.EntityUtilExtensions.SetPropertyValue(childTable, refitem, otm.Item1.RefColumns[a].CsName, colval);
594-
}
595-
return refitem;
596-
}).ToList();
597-
var childs = await childDbSet.Select.Where(commonUtils.WhereItems(otm.Item1.RefColumns.ToArray(), "a.", refitems)).ToListAsync(false, cancellationToken);
598-
await LocalEachAsync(childDbSet, childs, true);
599-
}
600-
}
601-
602-
var mtms = navs.Where(a => a.Item1.RefType == TableRefType.ManyToMany).ToList();
603-
if (mtms.Any())
604-
{
605-
foreach (var mtm in mtms)
606-
{
607-
var childTable = fsql.CodeFirst.GetTableByEntity(mtm.Item1.RefMiddleEntityType);
608-
var childDbSet = _db.Set<object>();
609-
childDbSet.AsType(mtm.Item1.RefMiddleEntityType);
610-
var miditems = items.Select(item =>
611-
{
612-
var refitem = mtm.Item1.RefMiddleEntityType.CreateInstanceGetDefaultValue();
613-
for (var a = 0; a < mtm.Item1.Columns.Count; a++)
614-
{
615-
var colval = FreeSql.Extensions.EntityUtil.EntityUtilExtensions.GetPropertyValue(tb, item, mtm.Item1.Columns[a].CsName);
616-
FreeSql.Extensions.EntityUtil.EntityUtilExtensions.SetPropertyValue(childTable, refitem, mtm.Item1.MiddleColumns[a].CsName, colval);
617-
}
618-
return refitem;
619-
}).ToList();
620-
var childs = await childDbSet.Select.Where(commonUtils.WhereItems(mtm.Item1.MiddleColumns.Take(mtm.Item1.Columns.Count).ToArray(), "a.", miditems)).ToListAsync(false, cancellationToken);
621-
await LocalEachAsync(childDbSet, childs, true);
622-
}
623-
}
624-
625-
if (dbset == rootDbSet)
626-
{
627-
if (CanRemove(data, true) == false) return;
628-
foreach (var item in data) //防止清除 Identity/Guid
629-
{
630-
var state = CreateEntityState(item);
631-
_states.TryRemove(state.Key, out var trystate);
632-
633-
EnqueueToDbContext(DbContext.EntityChangeType.Delete, state);
634-
}
635-
await DbContextFlushCommandAsync(cancellationToken);
636-
}
637-
else
638-
{
639-
if (dbset.CanRemove(items, true) == false) return;
640-
foreach (var item in items) //防止清除 Identity/Guid
641-
{
642-
var state = dbset.CreateEntityState(item);
643-
dbset._states.TryRemove(state.Key, out var trystate);
644-
645-
dbset.EnqueueToDbContext(DbContext.EntityChangeType.Delete, state);
646-
}
647-
await DbContextFlushCommandAsync(cancellationToken);
648-
}
649-
returnDeleted.AddRange(items);
650-
}
505+
//临时调用同步方法,后续会改
506+
return await Task.FromResult(RemoveRangeCascadeByMemoryOrDatabase(data, inMemory));
651507
}
652508
#endregion
653509
}

0 commit comments

Comments
 (0)