Skip to content

Commit cdd9587

Browse files
committed
ITestIsolationOptions.DeleteData uses entity name to identify the correct DbSet.
1 parent 5c5ce8a commit cdd9587

File tree

2 files changed

+36
-20
lines changed

2 files changed

+36
-20
lines changed

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<Copyright>(c) $([System.DateTime]::Now.Year), Pawel Gerr. All rights reserved.</Copyright>
5-
<VersionPrefix>7.2.1</VersionPrefix>
5+
<VersionPrefix>7.2.2</VersionPrefix>
66
<Authors>Pawel Gerr</Authors>
77
<GenerateDocumentationFile>true</GenerateDocumentationFile>
88
<PackageProjectUrl>https://dev.azure.com/pawelgerr/Thinktecture.EntityFrameworkCore</PackageProjectUrl>

src/Thinktecture.EntityFrameworkCore.SqlServer.Testing/EntityFrameworkCore/Testing/ITestIsolationOptions.cs

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,14 @@ public interface ITestIsolationOptions
5151
/// Deletes all records from the tables using "DELETE".
5252
/// No ambient transaction; no unique schema.
5353
/// </summary>
54-
public static ITestIsolationOptions DeleteData(Predicate<IEntityType>? filter = null)
54+
public static ITestIsolationOptions DeleteData(
55+
Predicate<IEntityType>? filter = null,
56+
Func<IReadOnlyList<IEntityType>, IReadOnlyList<IEntityType>>? modifyDeletionOrder = null)
5557
{
5658
return new DeleteAllData(filter is null
5759
? SkipTempTablesAndCollectionParameters
58-
: entityType => filter(entityType) && SkipTempTablesAndCollectionParameters(entityType));
60+
: entityType => filter(entityType) && SkipTempTablesAndCollectionParameters(entityType),
61+
modifyDeletionOrder);
5962
}
6063

6164
private static bool SkipTempTablesAndCollectionParameters(IEntityType entityType)
@@ -188,52 +191,65 @@ public async ValueTask CleanupAsync(DbContext dbContext, string? schema, Cancell
188191

189192
private class DeleteAllData : ITestIsolationOptions
190193
{
191-
private readonly Predicate<IEntityType> _filter;
192-
193194
private static readonly MethodInfo _deleteData = typeof(DeleteAllData).GetMethod(nameof(DeleteDataAsync), BindingFlags.Static | BindingFlags.NonPublic)
194195
?? throw new Exception($"Method '{nameof(DeleteDataAsync)}' not found.");
195196

196-
private readonly ConcurrentDictionary<Type, Func<DbContext, CancellationToken, Task>> _deleteDelegatesLookup;
197+
private readonly Predicate<IEntityType> _filter;
198+
private readonly Func<IReadOnlyList<IEntityType>, IReadOnlyList<IEntityType>>? _modifyDeletionOrder;
199+
private readonly ConcurrentDictionary<IEntityType, Func<DbContext, string, CancellationToken, Task>> _deleteDelegatesLookup;
200+
201+
private IReadOnlyList<IEntityType>? _orderedEntities;
197202

198203
public bool NeedsAmbientTransaction => false;
199204
public bool NeedsUniqueSchema => false;
200205
public bool NeedsCleanup => true;
201206

202-
public DeleteAllData(Predicate<IEntityType> filter)
207+
public DeleteAllData(
208+
Predicate<IEntityType> filter,
209+
Func<IReadOnlyList<IEntityType>, IReadOnlyList<IEntityType>>? modifyDeletionOrder)
203210
{
204211
_filter = filter;
205-
_deleteDelegatesLookup = new ConcurrentDictionary<Type, Func<DbContext, CancellationToken, Task>>();
212+
_modifyDeletionOrder = modifyDeletionOrder;
213+
_deleteDelegatesLookup = new ConcurrentDictionary<IEntityType, Func<DbContext, string, CancellationToken, Task>>();
206214
}
207215

208216
[SuppressMessage("Usage", "EF1001:Internal EF Core API usage.")]
209217
public async ValueTask CleanupAsync(DbContext dbContext, string? schema, CancellationToken cancellationToken)
210218
{
211-
foreach (var entityType in dbContext.Model.GetEntityTypesInHierarchicalOrder().Reverse())
219+
if (_orderedEntities is null)
212220
{
213-
if (entityType.GetTableName() is not null && _filter(entityType))
214-
{
215-
var delete = _deleteDelegatesLookup.GetOrAdd(entityType.ClrType, CreateDelegate);
221+
var orderedEntities = dbContext.Model.GetEntityTypesInHierarchicalOrder().Reverse().ToList();
222+
223+
_orderedEntities = _modifyDeletionOrder?.Invoke(orderedEntities) ?? orderedEntities;
224+
}
225+
226+
foreach (var entityType in _orderedEntities)
227+
{
228+
if (entityType.GetTableName() is null || !_filter(entityType))
229+
continue;
230+
231+
var delete = _deleteDelegatesLookup.GetOrAdd(entityType, CreateDelegate);
216232

217-
await delete(dbContext, cancellationToken);
218-
}
233+
await delete(dbContext, entityType.Name, cancellationToken);
219234
}
220235
}
221236

222-
private Func<DbContext, CancellationToken, Task> CreateDelegate(Type type)
237+
private static Func<DbContext, string, CancellationToken, Task> CreateDelegate(IEntityType type)
223238
{
224239
var ctxParam = Expression.Parameter(typeof(DbContext));
240+
var nameParam = Expression.Parameter(typeof(string));
225241
var cancellationTokenParam = Expression.Parameter(typeof(CancellationToken));
226-
var method = _deleteData.MakeGenericMethod(type);
242+
var method = _deleteData.MakeGenericMethod(type.ClrType);
227243

228-
var call = Expression.Call(method, ctxParam, cancellationTokenParam);
244+
var call = Expression.Call(method, ctxParam, nameParam, cancellationTokenParam);
229245

230-
return Expression.Lambda<Func<DbContext, CancellationToken, Task>>(call, ctxParam, cancellationTokenParam).Compile();
246+
return Expression.Lambda<Func<DbContext, string, CancellationToken, Task>>(call, ctxParam, nameParam, cancellationTokenParam).Compile();
231247
}
232248

233-
private static async Task DeleteDataAsync<T>(DbContext dbContext, CancellationToken cancellationToken)
249+
private static async Task DeleteDataAsync<T>(DbContext dbContext, string name, CancellationToken cancellationToken)
234250
where T : class
235251
{
236-
await dbContext.Set<T>().ExecuteDeleteAsync(cancellationToken);
252+
await dbContext.Set<T>(name).ExecuteDeleteAsync(cancellationToken);
237253
}
238254
}
239255

0 commit comments

Comments
 (0)