Retrying User Transactions with Database in Entity Framework 6 (Connection Resiliency)











up vote
1
down vote

favorite
1












I am attempting to implement retries on my database connections, which I've gotten working, but it's broken the user-initiated transactions I have as well.
Specifically, I see errors like the following:




The configured execution strategy 'RetryExecutionStrategy' does not support user initiated transactions. See http://go.microsoft.com/fwlink/?LinkId=309381 for additional information.




I would very much like to implement the first workaround from Microsoft, but even the second one would be enough. Despite many attempts from different angles, I've been unable to get things working for transactions. I've tried searching existing questions on StackOverflow (and the rest of the internet, really), but to no avail. Perhaps the solutions I've found elsewhere work for single threads with only one connection at a time, but I'm working on a large project where there are both transactions and non-transactions occurring in no predictable order.



Ultimately, what I need is a way to turn off retries for transactions or a fix to make transactions not crash when retrying is turned on.



What I have for my DbContext:



[DbConfigurationType("MyNamespace.RetryConfiguration","MyNamespace")]
public partial class RepoContext : DbContext {
public RepoContext(string entityConnectionString) : base(entityConnectionString)
{
}
}


My RetryExecutionStrategy:



public class RetryExecutionStrategy : DbExecutionStrategy
{
public RetryExecutionStrategy(int maxRetries, TimeSpan maxDelay)
: base(maxRetries, maxDelay)
{
}

protected override bool ShouldRetryOn(Exception e)
{
return true;
}
}


My RetryConfiguration:



public class RetryConfiguration : DbConfiguration
{
public RetryConfiguration()
{
var executionStrategy = SuspendExecutionStrategy
? (IDbExecutionStrategy)new DefaultExecutionStrategy()
: new RetryExecutionStrategy(3, new TimeSpan(0, 0, 0, 3));
this.SetExecutionStrategy("Devart.Data.PostgreSql", () => executionStrategy);
}

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


Unless I am missing something, I believe I implemented Microsoft's example correctly. From my understanding of their example, EF should make a new instance of RetryConfiguration for every repo call. Otherwise, their example doesn't make any sense as it would only ever create a connection with RetryExecutionStrategy rather than DefaultExecutionStrategy, which is needed for user-initiated transactions. Contrary to that, I've stuck a breakpoint on the constructor of RetryConfiguration and discovered it's only ever instantiated once.



This has led to a lot of headaches and attempts at fulfilling their second workaround like the following:



var res = new RetryExecutionStrategy(0, new TimeSpan(0,0,0));
RetryConfiguration.SuspendExecutionStrategy = true;
res.Execute(() =>
{
using (var trans = _repoFactory.GetTransactableRepository())
{
trans.BeginTransaction();
var entry = trans.GetAll<MyTable>().First();
entry.Alive = true;
trans.Save();
trans.Commit();
}
});
RetryConfiguration.SuspendExecutionStrategy = false;


I even tried manually calling DefaultExecutionStrategy.Execute().



var des = new System.Data.Entity.Infrastructure.DefaultExecutionStrategy();
des.Execute(() =>
{
using (var trans = _repoFactory.GetTransactableRepository())
{
trans.BeginTransaction();
var entry = trans.GetAll<MyTable>().First();
entry.Alive = true;
trans.Save();
trans.Commit();
}
});


Even in that situation I get an exception saying that RetryConfiguration does not allow user-initiated transactions.



For what it's worth, I tried adding RetryConfiguration.SuspendExecutionStrategy = true/false to our TransactableRepository class's BeginTransaction(), Commit(), and Rollback() functions. The class itself is just a wrapper for the connection. I was unsurprised when it didn't work, given that Microsoft's example only ever shows it being read from on RetryConfiguration's constructor, but I figured it was worth a try since I'm not terribly familiar with CallContext.










share|improve this question




























    up vote
    1
    down vote

    favorite
    1












    I am attempting to implement retries on my database connections, which I've gotten working, but it's broken the user-initiated transactions I have as well.
    Specifically, I see errors like the following:




    The configured execution strategy 'RetryExecutionStrategy' does not support user initiated transactions. See http://go.microsoft.com/fwlink/?LinkId=309381 for additional information.




    I would very much like to implement the first workaround from Microsoft, but even the second one would be enough. Despite many attempts from different angles, I've been unable to get things working for transactions. I've tried searching existing questions on StackOverflow (and the rest of the internet, really), but to no avail. Perhaps the solutions I've found elsewhere work for single threads with only one connection at a time, but I'm working on a large project where there are both transactions and non-transactions occurring in no predictable order.



    Ultimately, what I need is a way to turn off retries for transactions or a fix to make transactions not crash when retrying is turned on.



    What I have for my DbContext:



    [DbConfigurationType("MyNamespace.RetryConfiguration","MyNamespace")]
    public partial class RepoContext : DbContext {
    public RepoContext(string entityConnectionString) : base(entityConnectionString)
    {
    }
    }


    My RetryExecutionStrategy:



    public class RetryExecutionStrategy : DbExecutionStrategy
    {
    public RetryExecutionStrategy(int maxRetries, TimeSpan maxDelay)
    : base(maxRetries, maxDelay)
    {
    }

    protected override bool ShouldRetryOn(Exception e)
    {
    return true;
    }
    }


    My RetryConfiguration:



    public class RetryConfiguration : DbConfiguration
    {
    public RetryConfiguration()
    {
    var executionStrategy = SuspendExecutionStrategy
    ? (IDbExecutionStrategy)new DefaultExecutionStrategy()
    : new RetryExecutionStrategy(3, new TimeSpan(0, 0, 0, 3));
    this.SetExecutionStrategy("Devart.Data.PostgreSql", () => executionStrategy);
    }

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


    Unless I am missing something, I believe I implemented Microsoft's example correctly. From my understanding of their example, EF should make a new instance of RetryConfiguration for every repo call. Otherwise, their example doesn't make any sense as it would only ever create a connection with RetryExecutionStrategy rather than DefaultExecutionStrategy, which is needed for user-initiated transactions. Contrary to that, I've stuck a breakpoint on the constructor of RetryConfiguration and discovered it's only ever instantiated once.



    This has led to a lot of headaches and attempts at fulfilling their second workaround like the following:



    var res = new RetryExecutionStrategy(0, new TimeSpan(0,0,0));
    RetryConfiguration.SuspendExecutionStrategy = true;
    res.Execute(() =>
    {
    using (var trans = _repoFactory.GetTransactableRepository())
    {
    trans.BeginTransaction();
    var entry = trans.GetAll<MyTable>().First();
    entry.Alive = true;
    trans.Save();
    trans.Commit();
    }
    });
    RetryConfiguration.SuspendExecutionStrategy = false;


    I even tried manually calling DefaultExecutionStrategy.Execute().



    var des = new System.Data.Entity.Infrastructure.DefaultExecutionStrategy();
    des.Execute(() =>
    {
    using (var trans = _repoFactory.GetTransactableRepository())
    {
    trans.BeginTransaction();
    var entry = trans.GetAll<MyTable>().First();
    entry.Alive = true;
    trans.Save();
    trans.Commit();
    }
    });


    Even in that situation I get an exception saying that RetryConfiguration does not allow user-initiated transactions.



    For what it's worth, I tried adding RetryConfiguration.SuspendExecutionStrategy = true/false to our TransactableRepository class's BeginTransaction(), Commit(), and Rollback() functions. The class itself is just a wrapper for the connection. I was unsurprised when it didn't work, given that Microsoft's example only ever shows it being read from on RetryConfiguration's constructor, but I figured it was worth a try since I'm not terribly familiar with CallContext.










    share|improve this question


























      up vote
      1
      down vote

      favorite
      1









      up vote
      1
      down vote

      favorite
      1






      1





      I am attempting to implement retries on my database connections, which I've gotten working, but it's broken the user-initiated transactions I have as well.
      Specifically, I see errors like the following:




      The configured execution strategy 'RetryExecutionStrategy' does not support user initiated transactions. See http://go.microsoft.com/fwlink/?LinkId=309381 for additional information.




      I would very much like to implement the first workaround from Microsoft, but even the second one would be enough. Despite many attempts from different angles, I've been unable to get things working for transactions. I've tried searching existing questions on StackOverflow (and the rest of the internet, really), but to no avail. Perhaps the solutions I've found elsewhere work for single threads with only one connection at a time, but I'm working on a large project where there are both transactions and non-transactions occurring in no predictable order.



      Ultimately, what I need is a way to turn off retries for transactions or a fix to make transactions not crash when retrying is turned on.



      What I have for my DbContext:



      [DbConfigurationType("MyNamespace.RetryConfiguration","MyNamespace")]
      public partial class RepoContext : DbContext {
      public RepoContext(string entityConnectionString) : base(entityConnectionString)
      {
      }
      }


      My RetryExecutionStrategy:



      public class RetryExecutionStrategy : DbExecutionStrategy
      {
      public RetryExecutionStrategy(int maxRetries, TimeSpan maxDelay)
      : base(maxRetries, maxDelay)
      {
      }

      protected override bool ShouldRetryOn(Exception e)
      {
      return true;
      }
      }


      My RetryConfiguration:



      public class RetryConfiguration : DbConfiguration
      {
      public RetryConfiguration()
      {
      var executionStrategy = SuspendExecutionStrategy
      ? (IDbExecutionStrategy)new DefaultExecutionStrategy()
      : new RetryExecutionStrategy(3, new TimeSpan(0, 0, 0, 3));
      this.SetExecutionStrategy("Devart.Data.PostgreSql", () => executionStrategy);
      }

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


      Unless I am missing something, I believe I implemented Microsoft's example correctly. From my understanding of their example, EF should make a new instance of RetryConfiguration for every repo call. Otherwise, their example doesn't make any sense as it would only ever create a connection with RetryExecutionStrategy rather than DefaultExecutionStrategy, which is needed for user-initiated transactions. Contrary to that, I've stuck a breakpoint on the constructor of RetryConfiguration and discovered it's only ever instantiated once.



      This has led to a lot of headaches and attempts at fulfilling their second workaround like the following:



      var res = new RetryExecutionStrategy(0, new TimeSpan(0,0,0));
      RetryConfiguration.SuspendExecutionStrategy = true;
      res.Execute(() =>
      {
      using (var trans = _repoFactory.GetTransactableRepository())
      {
      trans.BeginTransaction();
      var entry = trans.GetAll<MyTable>().First();
      entry.Alive = true;
      trans.Save();
      trans.Commit();
      }
      });
      RetryConfiguration.SuspendExecutionStrategy = false;


      I even tried manually calling DefaultExecutionStrategy.Execute().



      var des = new System.Data.Entity.Infrastructure.DefaultExecutionStrategy();
      des.Execute(() =>
      {
      using (var trans = _repoFactory.GetTransactableRepository())
      {
      trans.BeginTransaction();
      var entry = trans.GetAll<MyTable>().First();
      entry.Alive = true;
      trans.Save();
      trans.Commit();
      }
      });


      Even in that situation I get an exception saying that RetryConfiguration does not allow user-initiated transactions.



      For what it's worth, I tried adding RetryConfiguration.SuspendExecutionStrategy = true/false to our TransactableRepository class's BeginTransaction(), Commit(), and Rollback() functions. The class itself is just a wrapper for the connection. I was unsurprised when it didn't work, given that Microsoft's example only ever shows it being read from on RetryConfiguration's constructor, but I figured it was worth a try since I'm not terribly familiar with CallContext.










      share|improve this question















      I am attempting to implement retries on my database connections, which I've gotten working, but it's broken the user-initiated transactions I have as well.
      Specifically, I see errors like the following:




      The configured execution strategy 'RetryExecutionStrategy' does not support user initiated transactions. See http://go.microsoft.com/fwlink/?LinkId=309381 for additional information.




      I would very much like to implement the first workaround from Microsoft, but even the second one would be enough. Despite many attempts from different angles, I've been unable to get things working for transactions. I've tried searching existing questions on StackOverflow (and the rest of the internet, really), but to no avail. Perhaps the solutions I've found elsewhere work for single threads with only one connection at a time, but I'm working on a large project where there are both transactions and non-transactions occurring in no predictable order.



      Ultimately, what I need is a way to turn off retries for transactions or a fix to make transactions not crash when retrying is turned on.



      What I have for my DbContext:



      [DbConfigurationType("MyNamespace.RetryConfiguration","MyNamespace")]
      public partial class RepoContext : DbContext {
      public RepoContext(string entityConnectionString) : base(entityConnectionString)
      {
      }
      }


      My RetryExecutionStrategy:



      public class RetryExecutionStrategy : DbExecutionStrategy
      {
      public RetryExecutionStrategy(int maxRetries, TimeSpan maxDelay)
      : base(maxRetries, maxDelay)
      {
      }

      protected override bool ShouldRetryOn(Exception e)
      {
      return true;
      }
      }


      My RetryConfiguration:



      public class RetryConfiguration : DbConfiguration
      {
      public RetryConfiguration()
      {
      var executionStrategy = SuspendExecutionStrategy
      ? (IDbExecutionStrategy)new DefaultExecutionStrategy()
      : new RetryExecutionStrategy(3, new TimeSpan(0, 0, 0, 3));
      this.SetExecutionStrategy("Devart.Data.PostgreSql", () => executionStrategy);
      }

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


      Unless I am missing something, I believe I implemented Microsoft's example correctly. From my understanding of their example, EF should make a new instance of RetryConfiguration for every repo call. Otherwise, their example doesn't make any sense as it would only ever create a connection with RetryExecutionStrategy rather than DefaultExecutionStrategy, which is needed for user-initiated transactions. Contrary to that, I've stuck a breakpoint on the constructor of RetryConfiguration and discovered it's only ever instantiated once.



      This has led to a lot of headaches and attempts at fulfilling their second workaround like the following:



      var res = new RetryExecutionStrategy(0, new TimeSpan(0,0,0));
      RetryConfiguration.SuspendExecutionStrategy = true;
      res.Execute(() =>
      {
      using (var trans = _repoFactory.GetTransactableRepository())
      {
      trans.BeginTransaction();
      var entry = trans.GetAll<MyTable>().First();
      entry.Alive = true;
      trans.Save();
      trans.Commit();
      }
      });
      RetryConfiguration.SuspendExecutionStrategy = false;


      I even tried manually calling DefaultExecutionStrategy.Execute().



      var des = new System.Data.Entity.Infrastructure.DefaultExecutionStrategy();
      des.Execute(() =>
      {
      using (var trans = _repoFactory.GetTransactableRepository())
      {
      trans.BeginTransaction();
      var entry = trans.GetAll<MyTable>().First();
      entry.Alive = true;
      trans.Save();
      trans.Commit();
      }
      });


      Even in that situation I get an exception saying that RetryConfiguration does not allow user-initiated transactions.



      For what it's worth, I tried adding RetryConfiguration.SuspendExecutionStrategy = true/false to our TransactableRepository class's BeginTransaction(), Commit(), and Rollback() functions. The class itself is just a wrapper for the connection. I was unsurprised when it didn't work, given that Microsoft's example only ever shows it being read from on RetryConfiguration's constructor, but I figured it was worth a try since I'm not terribly familiar with CallContext.







      c# transactions entity-framework-6 postgresql-9.4 devart






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 7 at 21:37

























      asked Nov 7 at 15:24









      Volt Cruelerz

      186




      186





























          active

          oldest

          votes











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














           

          draft saved


          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53192498%2fretrying-user-transactions-with-database-in-entity-framework-6-connection-resil%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown






























          active

          oldest

          votes













          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes
















           

          draft saved


          draft discarded



















































           


          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53192498%2fretrying-user-transactions-with-database-in-entity-framework-6-connection-resil%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          這個網誌中的熱門文章

          Xamarin.form Move up view when keyboard appear

          Post-Redirect-Get with Spring WebFlux and Thymeleaf

          Anylogic : not able to use stopDelay()