EF6 Suspendable Execution Strategy

EF6 introduces the new connection resiliency feature that allows for automatic retries of failed database operations. I was recently writing some documentation for EF6 and came across a scenario where it would be useful to have a global switch to enable/disable retry logic. Such a switch isn’t built into EF6, although we do have an item on our backlog to possibly enable this in the future.

Fortunately it’s actually pretty easy to implement this flag yourself.

Note: The code in this post is based on the latest nightly build and does not work with EF6.0.0-beta1.

The Always-On Default

The easiest way to register an execution strategy in EF6 is to make use of code-based configuration. Here is a typical configuration class that you might write to enable the SqlAzureExecutionStrategy (an execution strategy that will retry the known retryable exceptions when working with SQL Azure).

using System.Data.Entity;
using System.Data.Entity.SqlServer;

namespace Demo
{
    public class MyConfiguration : DbConfiguration
    {
        public MyConfiguration()
        {
            this.SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
        }
    }
}

Allowing Suspension

Because EF will get a new execution strategy for every operation it performs we can introduce some conditional logic to return either the default (non-retrying) strategy or the SQL Azure (retying) strategy based on a switch.

Notice that we are getting/setting the value for the flag from CallContext. This ensures that our flag works correctly if we are using the new async query/save feature in EF6.

using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.SqlServer;
using System.Runtime.Remoting.Messaging;

namespace Demo
{
    public class MyConfiguration : DbConfiguration
    {
        public MyConfiguration()
        {
            this.SetExecutionStrategy("System.Data.SqlClient", () => SuspendExecutionStrategy
              ? (IDbExecutionStrategy)new DefaultExecutionStrategy()
              : new SqlAzureExecutionStrategy());
        }

        public static bool SuspendExecutionStrategy
        {
            get
            {
                return (bool?)CallContext.LogicalGetData("SuspendExecutionStrategy") ?? false;
            }
            set
            {
                CallContext.LogicalSetData("SuspendExecutionStrategy", value);
            }
        }
    }
}

Using the Flag

Now we can use the flag to disable retry logic for certain operations.

using (var db = new BloggingContext())
{
  MyConfiguration.SuspendExecutionStrategy = true;

  // Perform without retry logic
  db.Blogs.Add(new Blog { Url = "romiller.com" });
  db.SaveChanges();

  MyConfiguration.SuspendExecutionStrategy = false;

}

When Would I Use This?

The most common reason you might use this is when performing an operation that is not supported by retry logic – such as user initiated transactions. This was the piece of documentation I was writing when I put the original code together, so you can find more info on this particular scenario on our MSDN site.