question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Possible breaking change from 5.0.12 to 6.0.1 when using ExecutionStrategy property on RelationalDbContextOptionsBuilder

See original GitHub issue

Ask a question

We are using the ExecutionStrategy property on the RelationalDbContextOptionsBuilder to decide if we should use a retrying or a non retrying execution strategy. This is decided by evaluating a variable on the CallContext which is set to true before some special queries where we are using a transaction with the isolation level ReadUncommitted.

In version 5.0.12 the ExecutionStrategy gets created, when the transaction is created which allows us to create a non retrying strategy in the above mentioned case but in the last version (6.0.1 and 6.0.0) the ExecutionStrategy gets created when the DbContext is created.

This is a problem for us as we are using dependency injection to create the DbContext and only want to use the isolation level ReadUncommited for very special queries. These queries then run into an exception when runnning as the execution strategy has already been created.

Include your code

In the dependency injection:

UseSqlServer(connectionString,
     sqlServerOptions => {
         sqlServerOptions.ExecutionStrategy(dependencies => {
             if (DatabaseConfiguration.SuspendExecutionStrategy) {
                 return new NonRetryingExecutionStrategy(dependencies);
             }

             return new SqlServerRetryingExecutionStrategy(dependencies);
         });
     })

when trying an uncommitted read with a transaction

DatabaseConfiguration.SuspendExecutionStrategy = true;
using (var transaction = db.BeginTransaction(IsolationLevel.ReadUncommitted)) {
            try {
                var result = func(sourceToken); // Do stuff
                transaction.Commit();
                return result;
            } catch (Exception ex) {
                transaction?.Rollback();
                throw;
            }
        }
    }
DatabaseConfiguration.SuspendExecutionStrategy = false;

This results in an System.InvalidOperationException with the following message:

The configured execution strategy 'SqlServerRetryingExecutionStrategy' does not support user-initiated transactions. Use the execution strategy returned by 'DbContext.Database.CreateExecutionStrategy()' to execute all the operations in the transaction as a retriable unit.

Include provider and version information

EF Core version: 6.0.0 and 6.0.1 Database provider: Microsoft.EntityFrameworkCore.SqlServer Target framework: .NET 6.0

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:8 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
AndriySvyrydcommented, Jan 7, 2022

@TheRealNic Ok, then another alternative is to derive a custom strategy:

public abstract class MyExecutionStrategy : SqlServerRetryingExecutionStrategy
{
    public MyExecutionStrategy(
        ExecutionStrategyDependencies dependencies)
        : base(dependencies)
    {
    }

    public override bool RetriesOnFailure
        => !DatabaseConfiguration.SuspendExecutionStrategy && base.RetriesOnFailure;

    public override TResult Execute<TState, TResult>(
        TState state,
        Func<DbContext, TState, TResult> operation,
        Func<DbContext, TState, ExecutionResult<TResult>>? verifySucceeded)
        => DatabaseConfiguration.SuspendExecutionStrategy
            ? operation(Dependencies.CurrentContext.Context, state)
            : base.Execute(state, operation, verifySucceeded);

    public override Task<TResult> ExecuteAsync<TState, TResult>(
        TState state,
        Func<DbContext, TState, CancellationToken, Task<TResult>> operation,
        Func<DbContext, TState, CancellationToken, Task<ExecutionResult<TResult>>>? verifySucceeded,
        CancellationToken cancellationToken = default)
        => DatabaseConfiguration.SuspendExecutionStrategy
            ? operation(Dependencies.CurrentContext.Context, state, cancellationToken)
            : base.ExecuteAsync(state, operation, verifySucceeded, cancellationToken);
}
1reaction
taspeotiscommented, Jan 3, 2022

Separate to your issue you should wrap DatabaseConfiguration.SuspendExecutionStrategy = false; in a try/finally so it’s set back to false when an exception is thrown.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Breaking changes in EF Core 6.0
The following API and behavior changes have the potential to break existing applications updating to EF Core 6.0.
Read more >
6.0 Release Notes | Npgsql Documentation
Npgsql 6.0 brings some major breaking changes and is not a simple in-place upgrade. Carefully read the breaking change notes below and upgrade ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found