Search code examples
c#entity-frameworkazure-sql-database.net-4.8

EF6.5 SetExecutionStrategy vs SetTransactionHandler


I'm somewhow lost in the documentation for SetExecutionStrategy and SetTransactionHandler for SQL Azure. I'm using Entity Framework 6.5.1 in a .Net Framework 4.8 winforms app together with Microsoft.Data.SqlClient.

I want to define retries for building my connection as well as for the performed actions. documentation states: SqlAzureExecutionStrategy : An IDbExecutionStrategy that retries actions that throw exceptions caused by SQL Azure transient failures.

Does this mean, that connections and transactions are handled by this?

What I have so far:

public class DataContextConfiguration : MicrosoftSqlDbConfiguration
{
    public DataContextConfiguration()
    {          
        SetProviderFactory(MicrosoftSqlProviderServices.ProviderInvariantName, Microsoft.Data.SqlClient.SqlClientFactory.Instance);
        SetProviderServices(MicrosoftSqlProviderServices.ProviderInvariantName, MicrosoftSqlProviderServices.Instance);                              
        SetExecutionStrategy(MicrosoftSqlProviderServices.ProviderInvariantName, () => new MicrosoftSqlAzureExecutionStrategy());         
    }
 }

[DbConfigurationType(typeof(DataContextConfiguration))]
public partial class ExternalModel : DbContext
{
    public ExternalModel(): base("name=ExternalModel"){}
    public ExternalModel(string connectionString) : base(connectionString){}    
}

My update code (simplified, more complex statements available):

try
{
    //update Table
    using (var db = new ExternalModel(ConnectionString))
    {
        exampledataset.value= true;                                
        db.SaveChanges();
    }
}
catch (Exception ex)
{
    MyException = ex.Message;
    Result = DatabaseResult.UpdateError;   
    return null; 
}

It the db.SaveChanges also retried internally because of the SetExecutionStrategy or do I need this: this.SetTransactionHandler(MicrosoftSqlProviderServices.ProviderInvariantName,() => new CommitFailureHandler());

Or alternatively should I retry by using a do-while:

try
{
    //update Table
    using (var db = new ExternalModel(ConnectionString))
    {
        exampledataset.value= true;  
        bool saveFailed;
        do
        {
            saveFailed = false;
            try
            {
                db.SaveChanges();
            }
            catch (DbUpdateException ex)
            {
                saveFailed = true;
                ex.Entries.Single().Reload();
            }
        } while (saveFailed);
    }
}
catch (Exception ex){...}

Thanks for your help.


Solution

  • No. SaveChanges will be retried with just the ExecutionStrategy.

    See Connection resiliency and retry logic EF6

    A TransactionHandler is for a slightly different problem. There's a tiny window where a transaction could commit on the server, but the information that it committed never reaches the client. There is an optional TransactionHandler that will do extra work to discover the fate of that on retry.

    Without a custom TransactionHandler EF will simply retry the transaction, assuming it failed. Normally if the transaction actually succeeded, then the retry will fail and no real harm is done. But the CommitFailureHandler is there in case you need to prevent the retry in this case.