Skip to content

Commit 09b15ba

Browse files
committed
SqlServerTestDbContextProviderBuilder doesn't set the default schema "tests" by default anymore
1 parent 6fa2581 commit 09b15ba

File tree

6 files changed

+65
-54
lines changed

6 files changed

+65
-54
lines changed

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

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
using System.Diagnostics.CodeAnalysis;
12
using Microsoft.Data.SqlClient;
23
using Microsoft.EntityFrameworkCore.Infrastructure;
4+
using Microsoft.EntityFrameworkCore.Metadata.Internal;
35
using Microsoft.EntityFrameworkCore.Migrations;
46
using Microsoft.EntityFrameworkCore.Storage;
57

@@ -46,7 +48,7 @@ public interface ITestIsolationOptions
4648
/// <param name="cleanup">Callback that performs the actual cleanup.</param>
4749
/// <typeparam name="T">Type of the <see cref="DbContext"/></typeparam>
4850
/// <returns></returns>
49-
public static ITestIsolationOptions Custom<T>(bool needsUniqueSchema, Func<T, string, CancellationToken, Task> cleanup)
51+
public static ITestIsolationOptions Custom<T>(bool needsUniqueSchema, Func<T, string?, CancellationToken, Task> cleanup)
5052
where T : DbContext
5153
{
5254
return new CustomCleanup<T>(needsUniqueSchema, cleanup);
@@ -70,15 +72,15 @@ public static ITestIsolationOptions Custom<T>(bool needsUniqueSchema, Func<T, st
7072
/// <summary>
7173
/// Cleanup of the database.
7274
/// </summary>
73-
ValueTask CleanupAsync(DbContext dbContext, string schema, CancellationToken cancellationToken);
75+
ValueTask CleanupAsync(DbContext dbContext, string? schema, CancellationToken cancellationToken);
7476

7577
private class NoIsolation : ITestIsolationOptions
7678
{
7779
public bool NeedsAmbientTransaction => false;
7880
public bool NeedsUniqueSchema => false;
7981
public bool NeedsCleanup => false;
8082

81-
public ValueTask CleanupAsync(DbContext dbContext, string schema, CancellationToken cancellationToken)
83+
public ValueTask CleanupAsync(DbContext dbContext, string? schema, CancellationToken cancellationToken)
8284
{
8385
return ValueTask.CompletedTask;
8486
}
@@ -90,7 +92,7 @@ private class ShareTablesIsolation : ITestIsolationOptions
9092
public bool NeedsUniqueSchema => false;
9193
public bool NeedsCleanup => false;
9294

93-
public ValueTask CleanupAsync(DbContext dbContext, string schema, CancellationToken cancellationToken)
95+
public ValueTask CleanupAsync(DbContext dbContext, string? schema, CancellationToken cancellationToken)
9496
{
9597
return ValueTask.CompletedTask;
9698
}
@@ -102,7 +104,7 @@ private class RollbackMigrationsAndCleanupDatabase : ITestIsolationOptions
102104
public bool NeedsUniqueSchema => true;
103105
public bool NeedsCleanup => true;
104106

105-
public async ValueTask CleanupAsync(DbContext dbContext, string schema, CancellationToken cancellationToken)
107+
public async ValueTask CleanupAsync(DbContext dbContext, string? schema, CancellationToken cancellationToken)
106108
{
107109
await RollbackMigrationsAsync(dbContext, cancellationToken);
108110
await DeleteDatabaseObjectsAsync(dbContext, schema, cancellationToken);
@@ -115,7 +117,7 @@ private class CleanupDatabase : ITestIsolationOptions
115117
public bool NeedsUniqueSchema => true;
116118
public bool NeedsCleanup => true;
117119

118-
public async ValueTask CleanupAsync(DbContext dbContext, string schema, CancellationToken cancellationToken)
120+
public async ValueTask CleanupAsync(DbContext dbContext, string? schema, CancellationToken cancellationToken)
119121
{
120122
await DeleteDatabaseObjectsAsync(dbContext, schema, cancellationToken);
121123
}
@@ -124,33 +126,34 @@ public async ValueTask CleanupAsync(DbContext dbContext, string schema, Cancella
124126
private class CustomCleanup<T> : ITestIsolationOptions
125127
where T : DbContext
126128
{
127-
private readonly Func<T, string, CancellationToken, Task> _cleanup;
129+
private readonly Func<T, string?, CancellationToken, Task> _cleanup;
128130

129131
public bool NeedsAmbientTransaction => false;
130132
public bool NeedsUniqueSchema { get; }
131133
public bool NeedsCleanup => true;
132134

133-
public CustomCleanup(bool needsUniqueSchema, Func<T, string, CancellationToken, Task> cleanup)
135+
public CustomCleanup(bool needsUniqueSchema, Func<T, string?, CancellationToken, Task> cleanup)
134136
{
135137
NeedsUniqueSchema = needsUniqueSchema;
136138
_cleanup = cleanup;
137139
}
138140

139-
public async ValueTask CleanupAsync(DbContext dbContext, string schema, CancellationToken cancellationToken)
141+
public async ValueTask CleanupAsync(DbContext dbContext, string? schema, CancellationToken cancellationToken)
140142
{
141143
await _cleanup((T)dbContext, schema, cancellationToken);
142144
}
143145
}
144146

147+
[SuppressMessage("Usage", "EF1001:Internal EF Core API usage.")]
145148
private class TruncateAllTables : ITestIsolationOptions
146149
{
147150
public bool NeedsAmbientTransaction => false;
148151
public bool NeedsUniqueSchema => false;
149152
public bool NeedsCleanup => true;
150153

151-
public async ValueTask CleanupAsync(DbContext dbContext, string schema, CancellationToken cancellationToken)
154+
public async ValueTask CleanupAsync(DbContext dbContext, string? schema, CancellationToken cancellationToken)
152155
{
153-
foreach (var entityType in dbContext.Model.GetEntityTypes())
156+
foreach (var entityType in dbContext.Model.GetEntityTypesInHierarchicalOrder().Reverse())
154157
{
155158
if (entityType.GetTableName() is not null)
156159
await dbContext.TruncateTableAsync(entityType.ClrType, cancellationToken);
@@ -171,15 +174,17 @@ protected static async Task RollbackMigrationsAsync(DbContext dbContext, Cancell
171174
/// <summary>
172175
/// Deletes database objects (like tables) with provided <paramref name="schema"/>.
173176
/// </summary>
174-
protected static async Task DeleteDatabaseObjectsAsync(DbContext ctx, string schema, CancellationToken cancellationToken)
177+
protected static async Task DeleteDatabaseObjectsAsync(DbContext ctx, string? schema, CancellationToken cancellationToken)
175178
{
176179
ArgumentNullException.ThrowIfNull(ctx);
177-
ArgumentNullException.ThrowIfNull(schema);
178180

179-
var sqlHelper = ctx.GetService<ISqlGenerationHelper>();
181+
if (schema is not null)
182+
{
183+
var sqlHelper = ctx.GetService<ISqlGenerationHelper>();
180184

181-
await ctx.Database.ExecuteSqlRawAsync(GetSqlForCleanup(), new object[] { new SqlParameter("@schema", schema) }, cancellationToken);
182-
await ctx.Database.ExecuteSqlRawAsync(GetDropSchemaSql(sqlHelper, schema), cancellationToken);
185+
await ctx.Database.ExecuteSqlRawAsync(GetSqlForCleanup(), new object[] { new SqlParameter("@schema", schema) }, cancellationToken);
186+
await ctx.Database.ExecuteSqlRawAsync(GetDropSchemaSql(sqlHelper, schema), cancellationToken);
187+
}
183188
}
184189

185190
private static string GetSqlForCleanup()

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class SqlServerTestDbContextProvider<T> : SqlServerTestDbContextProvider,
4040
private readonly IMigrationExecutionStrategy _migrationExecutionStrategy;
4141
private readonly DbConnection _masterConnection;
4242
private readonly IReadOnlyList<Action<T>> _contextInitializations;
43-
private readonly Func<DbContextOptions<T>, IDbDefaultSchema, T?>? _contextFactory;
43+
private readonly Func<DbContextOptions<T>, IDbDefaultSchema?, T?>? _contextFactory;
4444
private readonly TestingLoggingOptions _testingLoggingOptions;
4545

4646
private readonly bool _lockTableEnabled;
@@ -51,7 +51,7 @@ public class SqlServerTestDbContextProvider<T> : SqlServerTestDbContextProvider,
5151
private readonly TimeSpan _maxRetryDelay;
5252
private readonly Random _random;
5353

54-
private Func<DbContextOptions<T>, IDbDefaultSchema, T>? _defaultContextFactory;
54+
private Func<DbContextOptions<T>, IDbDefaultSchema?, T>? _defaultContextFactory;
5555
private T? _arrangeDbContext;
5656
private T? _actDbContext;
5757
private T? _assertDbContext;
@@ -70,10 +70,10 @@ public class SqlServerTestDbContextProvider<T> : SqlServerTestDbContextProvider,
7070
public T AssertDbContext => _assertDbContext ??= CreateDbContext(true);
7171

7272
/// <summary>
73-
/// Database schema to use.
73+
/// Default database schema to use.
7474
/// </summary>
7575
// ReSharper disable once MemberCanBePrivate.Global
76-
public string Schema { get; }
76+
public string? Schema { get; }
7777

7878
/// <summary>
7979
/// Contains executed commands if this feature was activated.
@@ -94,7 +94,7 @@ protected internal SqlServerTestDbContextProvider(SqlServerTestDbContextProvider
9494
ArgumentNullException.ThrowIfNull(options);
9595

9696
_instanceWideLock = new object();
97-
Schema = options.Schema ?? throw new ArgumentException($"The '{nameof(options.Schema)}' cannot be null.", nameof(options));
97+
Schema = options.Schema;
9898
_sharedTablesIsolationLevel = ValidateIsolationLevel(options.SharedTablesIsolationLevel);
9999
_isolationOptions = options.IsolationOptions;
100100
_masterConnection = options.MasterConnection ?? throw new ArgumentException($"The '{nameof(options.MasterConnection)}' cannot be null.", nameof(options));
@@ -157,7 +157,7 @@ public T CreateDbContext(bool useMasterConnection)
157157
}
158158

159159
var options = useMasterConnection ? _masterDbContextOptions : _dbContextOptions;
160-
var ctx = CreateDbContext(options, new DbDefaultSchema(Schema));
160+
var ctx = CreateDbContext(options, Schema is null ? null : new DbDefaultSchema(Schema));
161161

162162
foreach (var ctxInit in _contextInitializations)
163163
{
@@ -185,15 +185,15 @@ public T CreateDbContext(bool useMasterConnection)
185185
/// <param name="options">Options to use for creation.</param>
186186
/// <param name="schema">Database schema to use.</param>
187187
/// <returns>A new instance of the database context.</returns>
188-
protected virtual T CreateDbContext(DbContextOptions<T> options, IDbDefaultSchema schema)
188+
protected virtual T CreateDbContext(DbContextOptions<T> options, IDbDefaultSchema? schema)
189189
{
190190
var ctx = _contextFactory?.Invoke(options, schema)
191191
?? (_defaultContextFactory ??= CreateDefaultContextFactory())(options, schema);
192192

193193
return ctx;
194194
}
195195

196-
private static Func<DbContextOptions<T>, IDbDefaultSchema, T> CreateDefaultContextFactory()
196+
private static Func<DbContextOptions<T>, IDbDefaultSchema?, T> CreateDefaultContextFactory()
197197
{
198198
var optionsType = typeof(DbContextOptions<T>);
199199
var schemaType = typeof(IDbDefaultSchema);
@@ -221,7 +221,7 @@ private static Func<DbContextOptions<T>, IDbDefaultSchema, T> CreateDefaultConte
221221
Please provide the corresponding constructor or a custom factory via '{typeof(SqlServerTestDbContextProviderBuilder<T>).ShortDisplayName()}.{nameof(SqlServerTestDbContextProviderBuilder<T>.UseContextFactory)}'.");
222222
}
223223

224-
return Expression.Lambda<Func<DbContextOptions<T>, IDbDefaultSchema, T>>(Expression.New(ctor, ctorArgs), optionsParam, schemaParam)
224+
return Expression.Lambda<Func<DbContextOptions<T>, IDbDefaultSchema?, T>>(Expression.New(ctor, ctorArgs), optionsParam, schemaParam)
225225
.Compile();
226226
}
227227

@@ -416,7 +416,7 @@ private async Task DisposeContextsAndRollbackMigrationsAsync(CancellationToken c
416416
if (_isolationOptions.NeedsCleanup)
417417
{
418418
// Create a new ctx as a last resort to rollback migrations and clean up the database
419-
await using var ctx = _actDbContext ?? _arrangeDbContext ?? _assertDbContext ?? CreateDbContext(_masterDbContextOptions, new DbDefaultSchema(Schema));
419+
await using var ctx = _actDbContext ?? _arrangeDbContext ?? _assertDbContext ?? CreateDbContext(_masterDbContextOptions, Schema is null ? null : new DbDefaultSchema(Schema));
420420

421421
IDbContextTransaction? migrationTx = null;
422422

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

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ public class SqlServerTestDbContextProviderBuilder<T> : TestDbContextProviderBui
2020

2121
private readonly string _connectionString;
2222
private readonly ITestIsolationOptions _isolationOptions;
23-
private readonly List<Action<DbContextOptionsBuilder<T>, string>> _configuresOptionsCollection;
24-
private readonly List<Action<SqlServerDbContextOptionsBuilder, string>> _configuresSqlServerOptionsCollection;
23+
private readonly List<Action<DbContextOptionsBuilder<T>, string?>> _configuresOptionsCollection;
24+
private readonly List<Action<SqlServerDbContextOptionsBuilder, string?>> _configuresSqlServerOptionsCollection;
2525
private readonly List<Action<T>> _ctxInitializations;
2626

2727
private bool _useThinktectureSqlServerMigrationsSqlGenerator = true;
2828
private string? _sharedTablesSchema;
2929
private IsolationLevel? _sharedTablesIsolationLevel;
3030
private SqlServerLockTableOptions? _lockTable;
31-
private Func<DbContextOptions<T>, IDbDefaultSchema, T?>? _contextFactory;
31+
private Func<DbContextOptions<T>, IDbDefaultSchema?, T?>? _contextFactory;
3232
private Func<SqlServerTestDbContextProviderOptions<T>, SqlServerTestDbContextProvider<T>?>? _providerFactory;
3333

3434
/// <summary>
@@ -51,8 +51,8 @@ public SqlServerTestDbContextProviderBuilder(string connectionString, ITestIsola
5151
{
5252
_connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
5353
_isolationOptions = isolationOptions;
54-
_configuresOptionsCollection = new List<Action<DbContextOptionsBuilder<T>, string>>();
55-
_configuresSqlServerOptionsCollection = new List<Action<SqlServerDbContextOptionsBuilder, string>>();
54+
_configuresOptionsCollection = new List<Action<DbContextOptionsBuilder<T>, string?>>();
55+
_configuresSqlServerOptionsCollection = new List<Action<SqlServerDbContextOptionsBuilder, string?>>();
5656
_ctxInitializations = new List<Action<T>>();
5757
}
5858

@@ -175,7 +175,7 @@ public SqlServerTestDbContextProviderBuilder<T> UseSharedTablesIsolationLevel(Is
175175
/// </summary>
176176
/// <param name="configure">Callback is called with the current <see cref="DbContextOptionsBuilder{TContext}"/> and the database schema.</param>
177177
/// <returns>Current builder for chaining.</returns>
178-
public SqlServerTestDbContextProviderBuilder<T> ConfigureOptions(Action<DbContextOptionsBuilder<T>, string> configure)
178+
public SqlServerTestDbContextProviderBuilder<T> ConfigureOptions(Action<DbContextOptionsBuilder<T>, string?> configure)
179179
{
180180
ArgumentNullException.ThrowIfNull(configure);
181181

@@ -189,7 +189,7 @@ public SqlServerTestDbContextProviderBuilder<T> ConfigureOptions(Action<DbContex
189189
/// </summary>
190190
/// <param name="configure">Callback is called with the current <see cref="DbContextOptionsBuilder{TContext}"/> and the database schema.</param>
191191
/// <returns>Current builder for chaining.</returns>
192-
public SqlServerTestDbContextProviderBuilder<T> ConfigureSqlServerOptions(Action<SqlServerDbContextOptionsBuilder, string> configure)
192+
public SqlServerTestDbContextProviderBuilder<T> ConfigureSqlServerOptions(Action<SqlServerDbContextOptionsBuilder, string?> configure)
193193
{
194194
ArgumentNullException.ThrowIfNull(configure);
195195

@@ -267,7 +267,7 @@ public SqlServerTestDbContextProviderBuilder<T> InitializeContext(Action<T> init
267267
/// </summary>
268268
/// <param name="contextFactory">Factory to create the context of type <typeparamref name="T"/>.</param>
269269
/// <returns>Current builder for chaining.</returns>
270-
public SqlServerTestDbContextProviderBuilder<T> UseContextFactory(Func<DbContextOptions<T>, IDbDefaultSchema, T?>? contextFactory)
270+
public SqlServerTestDbContextProviderBuilder<T> UseContextFactory(Func<DbContextOptions<T>, IDbDefaultSchema?, T?>? contextFactory)
271271
{
272272
_contextFactory = contextFactory;
273273

@@ -292,7 +292,7 @@ public SqlServerTestDbContextProviderBuilder<T> UseProviderFactory(Func<SqlServe
292292
/// <param name="useSharedTables">Indication whether a new schema should be generated or a shared one.</param>
293293
/// <returns>A database schema.</returns>
294294
[Obsolete($"Use the overload with '{nameof(ITestIsolationOptions)}' instead.")]
295-
protected virtual string DetermineSchema(bool useSharedTables)
295+
protected virtual string? DetermineSchema(bool useSharedTables)
296296
{
297297
return DetermineSchema(useSharedTables ? ITestIsolationOptions.SharedTablesAmbientTransaction : ITestIsolationOptions.RollbackMigrationsAndCleanup);
298298
}
@@ -302,22 +302,22 @@ protected virtual string DetermineSchema(bool useSharedTables)
302302
/// </summary>
303303
/// <param name="isolationOptions">Test isolation behavior.</param>
304304
/// <returns>A database schema.</returns>
305-
protected virtual string DetermineSchema(ITestIsolationOptions isolationOptions)
305+
protected virtual string? DetermineSchema(ITestIsolationOptions isolationOptions)
306306
{
307307
return !isolationOptions.NeedsUniqueSchema
308-
? _sharedTablesSchema ?? "tests"
308+
? _sharedTablesSchema
309309
: Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
310310
}
311311

312312
/// <summary>
313313
/// Creates and configures the <see cref="DbContextOptionsBuilder{TContext}"/>
314314
/// </summary>
315315
/// <param name="connection">Database connection to use.</param>
316-
/// <param name="schema">Database schema to use.</param>
316+
/// <param name="schema">Default database schema to use.</param>
317317
/// <returns>An instance of <see cref="DbContextOptionsBuilder{TContext}"/></returns>
318318
public virtual DbContextOptionsBuilder<T> CreateOptionsBuilder(
319319
DbConnection? connection,
320-
string schema)
320+
string? schema)
321321
{
322322
var loggingOptions = CreateLoggingOptions();
323323
var state = new TestDbContextProviderBuilderState(loggingOptions);
@@ -330,12 +330,12 @@ public virtual DbContextOptionsBuilder<T> CreateOptionsBuilder(
330330
/// </summary>
331331
/// <param name="state">Current building state.</param>
332332
/// <param name="connection">Database connection to use.</param>
333-
/// <param name="schema">Database schema to use.</param>
333+
/// <param name="schema">Default database schema to use.</param>
334334
/// <returns>An instance of <see cref="DbContextOptionsBuilder{TContext}"/></returns>
335335
protected virtual DbContextOptionsBuilder<T> CreateOptionsBuilder(
336336
TestDbContextProviderBuilderState state,
337337
DbConnection? connection,
338-
string schema)
338+
string? schema)
339339
{
340340
var builder = new DbContextOptionsBuilder<T>();
341341

@@ -348,7 +348,8 @@ protected virtual DbContextOptionsBuilder<T> CreateOptionsBuilder(
348348
builder.UseSqlServer(connection, optionsBuilder => ConfigureSqlServer(optionsBuilder, schema));
349349
}
350350

351-
builder.AddSchemaRespectingComponents();
351+
if (schema is not null)
352+
builder.AddSchemaRespectingComponents();
352353

353354
ApplyDefaultConfiguration(state, builder);
354355

@@ -363,7 +364,7 @@ protected virtual DbContextOptionsBuilder<T> CreateOptionsBuilder(
363364
/// <param name="builder">A builder for configuration of the options.</param>
364365
/// <param name="schema">Schema to use</param>
365366
/// <exception cref="ArgumentNullException">The <paramref name="builder"/> is null.</exception>
366-
protected virtual void ConfigureSqlServer(SqlServerDbContextOptionsBuilder builder, string schema)
367+
protected virtual void ConfigureSqlServer(SqlServerDbContextOptionsBuilder builder, string? schema)
367368
{
368369
ArgumentNullException.ThrowIfNull(builder);
369370

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ public ITestIsolationOptions IsolationOptions
3535
}
3636

3737
/// <summary>
38-
/// Database schema to use.
38+
/// Default database schema to use.
3939
/// </summary>
40-
public string Schema { get; set; }
40+
public string? Schema { get; }
4141

4242
/// <summary>
4343
/// A factory method for creation of contexts of type <typeparamref name="T"/>.
4444
/// </summary>
45-
public Func<DbContextOptions<T>, IDbDefaultSchema, T?>? ContextFactory { get; set; }
45+
public Func<DbContextOptions<T>, IDbDefaultSchema?, T?>? ContextFactory { get; set; }
4646

4747
/// <summary>
4848
/// Isolation level to be used with shared tables.
@@ -72,7 +72,7 @@ public SqlServerTestDbContextProviderOptions(
7272
DbContextOptions<T> dbContextOptions,
7373
TestingLoggingOptions testingLoggingOptions,
7474
IReadOnlyList<Action<T>> contextInitializations,
75-
string schema)
75+
string? schema)
7676
: base(masterConnection, migrationExecutionStrategy, masterDbContextOptions, dbContextOptions, testingLoggingOptions, contextInitializations)
7777
{
7878
Schema = schema;

0 commit comments

Comments
 (0)