Search code examples
entity-framework-6

How to test entity framework execution strategy?


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.


Solution

  • 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)

    1. I added ExectuionStrategyBase.cs and TestServerRetryingExecutionStrategy.cs. The first one is clone of ExectuionStrategy.cs and second one is clone of SqlServerRetryingExecutionStrategy.cs

    2. 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);
                  });                   
              });
      
    3. 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.