We have an Entity Framework execution strategy coded in our lower environment. How do we test this to show it's actually working? We don't want to release to Prod without something to say we aren't introducing new problems.
The easy way is to use some listener where you can throw an exception and subscribe this listener to the dbContext
.
public class CommandListener
{
[DiagnosticName("Microsoft.EntityFrameworkCore.Database.Command.CommandExecuting")]
public void OnCommandExecuting(DbCommand command, DbCommandMethod executeMethod, Guid commandId, Guid connectionId, bool async, DateTimeOffset startTime)
{
throw new TimeoutException("Test exception");
}
[DiagnosticName("Microsoft.EntityFrameworkCore.Database.Command.CommandExecuted")]
public void OnCommandExecuted(object result, bool async)
{
}
[DiagnosticName("Microsoft.EntityFrameworkCore.Database.Command.CommandError")]
public void OnCommandError(Exception exception, bool async)
{
}
}
Subscribe listener to dbContext
f.i. in Startup.cs
var context = provider.GetService<SomeDbContext>();
var listener = context.GetService<DiagnosticSource>();
(listener as DiagnosticListener).SubscribeWithAdapter(new CommandListener());
As TimeoutException
is transient exception in SqlServerRetryingExecutionStrategy.cs
(if you use the default retrying strategy) you will get TimeoutException
as many as your MaxRetryingCount
of strategy setting has. Finally, you have to get RetryLimitExceededException
as a result of the request.
You have to see TimeoutException
in your app logs. Also it is good idea to turn on transaction logs "Microsoft.EntityFrameworkCore.Database.Transaction": "Debug"
What I did to manage to throw the transient exception and debug strategy (testing and playing around purpose only)
I added ExectuionStrategyBase.cs
and
TestServerRetryingExecutionStrategy.cs
. The first one is clone of
ExectuionStrategy.cs
and second one is clone of
SqlServerRetryingExecutionStrategy.cs
In Startup.cs I set retrying strategy
strategy services.AddDbContext<SomeDbContext>(options =>
{options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), sqlOption =>
{
sqlOption.ExecutionStrategy(dependencies =>
{
return new TestSqlRetryingStrategy(
dependencies,
settings.SqlMaxRetryCount,
settings.SqlMaxRetryDelay,
null);
});
});
In OnCommandExecuting
of CommandListener.sc
I just checked some static bool
variable to throw or not TimeoutException
and in ExectuionStrategyBase.cs
I swithced that variable.
Thus, I managed to throw transient Exception
on the first execution of the query and successful execution on the second short. Now I think about some long running transaction and kill session of this transaction in SSCM during execution of it.
Also, I found out that if there is a query like
var users = context.Users.AsNoTracking().ToArrayAsync()
execution strategy is not implemented and I am stuck on it. I have been struggling with that a couple of days but still can figure out nothing. If remove AsNoTracking
or replace ToArrayAsync()
by something like FirstAsync()
all foes well.