Skip to content

Commit 7a5b1a6

Browse files
committed
Testing: Moved common settings to TestDbContextProviderBuilder and moved command-capturing code to an interceptor.
1 parent 51d769b commit 7a5b1a6

File tree

11 files changed

+210
-153
lines changed

11 files changed

+210
-153
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public class SqlServerTestDbContextProvider<T> : ITestDbContextProvider<T>
5252
/// <summary>
5353
/// Contains executed commands if this feature was activated.
5454
/// </summary>
55-
public IReadOnlyCollection<string>? ExecutedCommands => _testingLoggingOptions.ExecutedCommands;
55+
public IReadOnlyCollection<string>? ExecutedCommands { get; }
5656

5757
/// <summary>
5858
/// Log level switch.
@@ -75,6 +75,7 @@ protected internal SqlServerTestDbContextProvider(SqlServerTestDbContextProvider
7575
_migrationExecutionStrategy = options.MigrationExecutionStrategy ?? throw new ArgumentException($"The '{nameof(options.MigrationExecutionStrategy)}' cannot be null.", nameof(options));
7676
_testingLoggingOptions = options.TestingLoggingOptions ?? throw new ArgumentException($"The '{nameof(options.TestingLoggingOptions)}' cannot be null.", nameof(options));
7777
_contextInitializations = options.ContextInitializations ?? throw new ArgumentException($"The '{nameof(options.ContextInitializations)}' cannot be null.", nameof(options));
78+
ExecutedCommands = options.ExecutedCommands;
7879
_contextFactory = options.ContextFactory;
7980
}
8081

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

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
using Microsoft.Data.SqlClient;
44
using Microsoft.EntityFrameworkCore.Infrastructure;
55
using Microsoft.Extensions.Logging;
6-
using Thinktecture.EntityFrameworkCore.Infrastructure;
76
using Thinktecture.EntityFrameworkCore.Migrations;
8-
using Thinktecture.Logging;
97
using Xunit.Abstractions;
108

119
namespace Thinktecture.EntityFrameworkCore.Testing;
@@ -25,8 +23,6 @@ public class SqlServerTestDbContextProviderBuilder<T> : TestDbContextProviderBui
2523
private readonly List<Action<SqlServerDbContextOptionsBuilder, string>> _configuresSqlServerOptionsCollection;
2624
private readonly List<Action<T>> _ctxInitializations;
2725

28-
private IMigrationExecutionStrategy? _migrationExecutionStrategy;
29-
private bool _disableModelCache;
3026
private bool _useThinktectureSqlServerMigrationsSqlGenerator = true;
3127
private string? _sharedTablesSchema;
3228
private Func<DbContextOptions<T>, IDbDefaultSchema, T>? _contextFactory;
@@ -51,11 +47,9 @@ public SqlServerTestDbContextProviderBuilder(string connectionString, bool useSh
5147
/// </summary>
5248
/// <param name="migrationExecutionStrategy">Migration strategy to use.</param>
5349
/// <returns>Current builder for chaining</returns>
54-
public SqlServerTestDbContextProviderBuilder<T> UseMigrationExecutionStrategy(IMigrationExecutionStrategy migrationExecutionStrategy)
50+
public new SqlServerTestDbContextProviderBuilder<T> UseMigrationExecutionStrategy(IMigrationExecutionStrategy migrationExecutionStrategy)
5551
{
56-
ArgumentNullException.ThrowIfNull(migrationExecutionStrategy);
57-
58-
_migrationExecutionStrategy = migrationExecutionStrategy;
52+
base.UseMigrationExecutionStrategy(migrationExecutionStrategy);
5953

6054
return this;
6155
}
@@ -136,9 +130,9 @@ public SqlServerTestDbContextProviderBuilder<T> ConfigureSqlServerOptions(Action
136130
/// Disables EF model cache.
137131
/// </summary>
138132
/// <returns>Current builder for chaining.</returns>
139-
public SqlServerTestDbContextProviderBuilder<T> DisableModelCache(bool disableModelCache = true)
133+
public new SqlServerTestDbContextProviderBuilder<T> DisableModelCache(bool disableModelCache = true)
140134
{
141-
_disableModelCache = disableModelCache;
135+
base.DisableModelCache(disableModelCache);
142136

143137
return this;
144138
}
@@ -231,21 +225,22 @@ public virtual DbContextOptionsBuilder<T> CreateOptionsBuilder(
231225
string schema)
232226
{
233227
var loggingOptions = CreateLoggingOptions();
228+
var state = new TestDbContextProviderBuilderState(loggingOptions);
234229

235-
return CreateOptionsBuilder(connection, schema, loggingOptions);
230+
return CreateOptionsBuilder(state, connection, schema);
236231
}
237232

238233
/// <summary>
239234
/// Creates and configures the <see cref="DbContextOptionsBuilder{TContext}"/>
240235
/// </summary>
236+
/// <param name="state">Current building state.</param>
241237
/// <param name="connection">Database connection to use.</param>
242238
/// <param name="schema">Database schema to use.</param>
243-
/// <param name="loggingOptions">Logging options.</param>
244239
/// <returns>An instance of <see cref="DbContextOptionsBuilder{TContext}"/></returns>
245240
protected virtual DbContextOptionsBuilder<T> CreateOptionsBuilder(
241+
TestDbContextProviderBuilderState state,
246242
DbConnection? connection,
247-
string schema,
248-
TestingLoggingOptions loggingOptions)
243+
string schema)
249244
{
250245
var builder = new DbContextOptionsBuilder<T>();
251246

@@ -258,14 +253,7 @@ protected virtual DbContextOptionsBuilder<T> CreateOptionsBuilder(
258253
builder.UseSqlServer(connection, optionsBuilder => ConfigureSqlServer(optionsBuilder, schema));
259254
}
260255

261-
builder.AddSchemaRespectingComponents()
262-
.UseLoggerFactory(loggingOptions.LoggerFactory)
263-
.EnableSensitiveDataLogging(loggingOptions.EnableSensitiveDataLogging);
264-
265-
DisableLoggingCacheTime(builder);
266-
267-
if (_disableModelCache)
268-
builder.ReplaceService<IModelCacheKeyFactory, CachePerContextModelCacheKeyFactory>();
256+
ApplyDefaultConfiguration(state, builder);
269257

270258
_configuresOptionsCollection.ForEach(configure => configure(builder, schema));
271259

@@ -304,19 +292,21 @@ public SqlServerTestDbContextProvider<T> Build()
304292
try
305293
{
306294
var loggingOptions = CreateLoggingOptions();
307-
var masterDbContextOptions = CreateOptionsBuilder(masterConnection, schema, loggingOptions).Options;
308-
var dbContextOptions = CreateOptionsBuilder(null, schema, loggingOptions).Options;
295+
var state = new TestDbContextProviderBuilderState(loggingOptions);
296+
var masterDbContextOptions = CreateOptionsBuilder(state, masterConnection, schema).Options;
297+
var dbContextOptions = CreateOptionsBuilder(state, null, schema).Options;
309298

310299
return new SqlServerTestDbContextProvider<T>(new SqlServerTestDbContextProviderOptions<T>(masterConnection,
311-
_migrationExecutionStrategy ?? IMigrationExecutionStrategy.Migrations,
300+
state.MigrationExecutionStrategy ?? IMigrationExecutionStrategy.Migrations,
312301
masterDbContextOptions,
313302
dbContextOptions,
314303
loggingOptions,
315304
_ctxInitializations.ToList(),
316305
schema)
317306
{
318307
IsUsingSharedTables = _useSharedTables,
319-
ContextFactory = _contextFactory
308+
ContextFactory = _contextFactory,
309+
ExecutedCommands = state.CommandCapturingInterceptor?.Commands
320310
});
321311
}
322312
catch

src/Thinktecture.EntityFrameworkCore.Sqlite.Testing/EntityFrameworkCore/Testing/SqliteTestDbContextProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public class SqliteTestDbContextProvider<T> : ITestDbContextProvider<T>
3939
/// <summary>
4040
/// Contains executed commands if this feature was activated.
4141
/// </summary>
42-
public IReadOnlyCollection<string>? ExecutedCommands => _testingLoggingOptions.ExecutedCommands;
42+
public IReadOnlyCollection<string>? ExecutedCommands { get; }
4343

4444
/// <summary>
4545
/// Log level switch.
@@ -66,6 +66,7 @@ protected internal SqliteTestDbContextProvider(SqliteTestDbContextProviderOption
6666
_migrationExecutionStrategy = options.MigrationExecutionStrategy ?? throw new ArgumentException($"The '{nameof(options.MigrationExecutionStrategy)}' cannot be null.", nameof(options));
6767
_testingLoggingOptions = options.TestingLoggingOptions ?? throw new ArgumentException($"The '{nameof(options.TestingLoggingOptions)}' cannot be null.", nameof(options));
6868
_contextInitializations = options.ContextInitializations ?? throw new ArgumentException($"The '{nameof(options.ContextInitializations)}' cannot be null.", nameof(options));
69+
ExecutedCommands = options.ExecutedCommands;
6970
_contextFactory = options.ContextFactory;
7071
}
7172

src/Thinktecture.EntityFrameworkCore.Sqlite.Testing/EntityFrameworkCore/Testing/SqliteTestDbContextProviderBuilder.cs

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
using Microsoft.Data.Sqlite;
33
using Microsoft.EntityFrameworkCore.Infrastructure;
44
using Microsoft.Extensions.Logging;
5-
using Thinktecture.EntityFrameworkCore.Infrastructure;
6-
using Thinktecture.Logging;
75
using Xunit.Abstractions;
86

97
namespace Thinktecture.EntityFrameworkCore.Testing;
@@ -19,8 +17,6 @@ public class SqliteTestDbContextProviderBuilder<T> : TestDbContextProviderBuilde
1917
private readonly List<Action<SqliteDbContextOptionsBuilder>> _configuresSqliteOptionsCollection;
2018
private readonly List<Action<T>> _ctxInitializations;
2119

22-
private IMigrationExecutionStrategy? _migrationExecutionStrategy;
23-
private bool _disableModelCache;
2420
private Func<DbContextOptions<T>, T>? _contextFactory;
2521

2622
/// <summary>
@@ -39,11 +35,9 @@ public SqliteTestDbContextProviderBuilder()
3935
/// </summary>
4036
/// <param name="migrationExecutionStrategy">Migration strategy to use.</param>
4137
/// <returns>Current builder for chaining</returns>
42-
public SqliteTestDbContextProviderBuilder<T> UseMigrationExecutionStrategy(IMigrationExecutionStrategy migrationExecutionStrategy)
38+
public new SqliteTestDbContextProviderBuilder<T> UseMigrationExecutionStrategy(IMigrationExecutionStrategy migrationExecutionStrategy)
4339
{
44-
ArgumentNullException.ThrowIfNull(migrationExecutionStrategy);
45-
46-
_migrationExecutionStrategy = migrationExecutionStrategy;
40+
base.UseMigrationExecutionStrategy(migrationExecutionStrategy);
4741

4842
return this;
4943
}
@@ -124,9 +118,9 @@ public SqliteTestDbContextProviderBuilder<T> ConfigureSqliteOptions(Action<Sqlit
124118
/// Disables EF model cache.
125119
/// </summary>
126120
/// <returns>Current builder for chaining.</returns>
127-
public SqliteTestDbContextProviderBuilder<T> DisableModelCache(bool disableModelCache = true)
121+
public new SqliteTestDbContextProviderBuilder<T> DisableModelCache(bool disableModelCache = true)
128122
{
129-
_disableModelCache = disableModelCache;
123+
base.DisableModelCache(disableModelCache);
130124

131125
return this;
132126
}
@@ -179,21 +173,22 @@ public virtual DbContextOptionsBuilder<T> CreateOptionsBuilder(
179173
string connectionString)
180174
{
181175
var loggingOptions = CreateLoggingOptions();
176+
var state = new TestDbContextProviderBuilderState(loggingOptions);
182177

183-
return CreateOptionsBuilder(connection, connectionString, loggingOptions);
178+
return CreateOptionsBuilder(state, connection, connectionString);
184179
}
185180

186181
/// <summary>
187182
/// Creates and configures the <see cref="DbContextOptionsBuilder{TContext}"/>
188183
/// </summary>
184+
/// <param name="state">Current building state.</param>
189185
/// <param name="connection">Database connection to use.</param>
190186
/// <param name="connectionString">Connection string to use.</param>
191-
/// <param name="loggingOptions">Logging options.</param>
192187
/// <returns>An instance of <see cref="DbContextOptionsBuilder{TContext}"/></returns>
193188
protected virtual DbContextOptionsBuilder<T> CreateOptionsBuilder(
189+
TestDbContextProviderBuilderState state,
194190
DbConnection? connection,
195-
string connectionString,
196-
TestingLoggingOptions loggingOptions)
191+
string connectionString)
197192
{
198193
var builder = new DbContextOptionsBuilder<T>();
199194

@@ -206,14 +201,7 @@ protected virtual DbContextOptionsBuilder<T> CreateOptionsBuilder(
206201
builder.UseSqlite(connection, ConfigureSqlite);
207202
}
208203

209-
builder.AddSchemaRespectingComponents()
210-
.UseLoggerFactory(loggingOptions.LoggerFactory)
211-
.EnableSensitiveDataLogging(loggingOptions.EnableSensitiveDataLogging);
212-
213-
DisableLoggingCacheTime(builder);
214-
215-
if (_disableModelCache)
216-
builder.ReplaceService<IModelCacheKeyFactory, CachePerContextModelCacheKeyFactory>();
204+
ApplyDefaultConfiguration(state, builder);
217205

218206
_configuresOptionsCollection.ForEach(configure => configure(builder));
219207

@@ -246,18 +234,20 @@ public SqliteTestDbContextProvider<T> Build()
246234
try
247235
{
248236
var loggingOptions = CreateLoggingOptions();
249-
var masterDbContextOptions = CreateOptionsBuilder(masterConnection, connectionString, loggingOptions).Options;
250-
var dbContextOptions = CreateOptionsBuilder(null, connectionString, loggingOptions).Options;
237+
var state = new TestDbContextProviderBuilderState(loggingOptions);
238+
var masterDbContextOptions = CreateOptionsBuilder(state, masterConnection, connectionString).Options;
239+
var dbContextOptions = CreateOptionsBuilder(state, null, connectionString).Options;
251240

252241
return new SqliteTestDbContextProvider<T>(new SqliteTestDbContextProviderOptions<T>(masterConnection,
253-
_migrationExecutionStrategy ?? IMigrationExecutionStrategy.Migrations,
242+
state.MigrationExecutionStrategy ?? IMigrationExecutionStrategy.Migrations,
254243
masterDbContextOptions,
255244
dbContextOptions,
256245
loggingOptions,
257246
_ctxInitializations.ToList(),
258247
connectionString)
259248
{
260-
ContextFactory = _contextFactory
249+
ContextFactory = _contextFactory,
250+
ExecutedCommands = state.CommandCapturingInterceptor?.Commands
261251
});
262252
}
263253
catch
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using System.Collections.Concurrent;
2+
using System.Data.Common;
3+
using Microsoft.EntityFrameworkCore.Diagnostics;
4+
5+
namespace Thinktecture.EntityFrameworkCore.Diagnostics;
6+
7+
/// <summary>
8+
/// Interceptor for capturing commands.
9+
/// </summary>
10+
public class CommandCapturingInterceptor : DbCommandInterceptor
11+
{
12+
private readonly ConcurrentQueue<string> _commands;
13+
14+
/// <summary>
15+
/// Captured commands.
16+
/// </summary>
17+
public IReadOnlyCollection<string> Commands => _commands;
18+
19+
/// <summary>
20+
/// Initializes new instance of <see cref="CommandCapturingInterceptor"/>.
21+
/// </summary>
22+
public CommandCapturingInterceptor()
23+
{
24+
_commands = new ConcurrentQueue<string>();
25+
}
26+
27+
/// <inheritdoc />
28+
public override DbDataReader ReaderExecuted(DbCommand command, CommandExecutedEventData eventData, DbDataReader result)
29+
{
30+
CaptureCommandText(command);
31+
32+
return base.ReaderExecuted(command, eventData, result);
33+
}
34+
35+
/// <inheritdoc />
36+
public override ValueTask<DbDataReader> ReaderExecutedAsync(DbCommand command, CommandExecutedEventData eventData, DbDataReader result, CancellationToken cancellationToken = new CancellationToken())
37+
{
38+
CaptureCommandText(command);
39+
40+
return base.ReaderExecutedAsync(command, eventData, result, cancellationToken);
41+
}
42+
43+
/// <inheritdoc />
44+
public override object? ScalarExecuted(DbCommand command, CommandExecutedEventData eventData, object? result)
45+
{
46+
CaptureCommandText(command);
47+
48+
return base.ScalarExecuted(command, eventData, result);
49+
}
50+
51+
/// <inheritdoc />
52+
public override ValueTask<object?> ScalarExecutedAsync(DbCommand command, CommandExecutedEventData eventData, object? result, CancellationToken cancellationToken = new CancellationToken())
53+
{
54+
CaptureCommandText(command);
55+
56+
return base.ScalarExecutedAsync(command, eventData, result, cancellationToken);
57+
}
58+
59+
/// <inheritdoc />
60+
public override int NonQueryExecuted(DbCommand command, CommandExecutedEventData eventData, int result)
61+
{
62+
CaptureCommandText(command);
63+
64+
return base.NonQueryExecuted(command, eventData, result);
65+
}
66+
67+
/// <inheritdoc />
68+
public override ValueTask<int> NonQueryExecutedAsync(DbCommand command, CommandExecutedEventData eventData, int result, CancellationToken cancellationToken = new CancellationToken())
69+
{
70+
CaptureCommandText(command);
71+
72+
return base.NonQueryExecutedAsync(command, eventData, result, cancellationToken);
73+
}
74+
75+
private void CaptureCommandText(DbCommand command)
76+
{
77+
_commands.Enqueue(command.CommandText);
78+
}
79+
}

0 commit comments

Comments
 (0)