首页 > 解决方案 > 在 Entity Framework 6 中重试与数据库的用户事务(连接弹性)

问题描述

我正在尝试在我已经开始工作的数据库连接上实现重试,但它也破坏了我拥有的用户启动的事务。具体来说,我看到如下错误:

配置的执行策略“RetryExecutionStrategy”不支持用户发起的事务。有关其他信息,请参阅http://go.microsoft.com/fwlink/?LinkId=309381

我非常想实施Microsoft的第一个解决方法,但即使是第二个也足够了。尽管从不同的角度进行了许多尝试,但我一直无法让事情为交易工作。我尝试在 StackOverflow(以及互联网的其他部分,真的)上搜索现有问题,但无济于事。也许我在其他地方找到的解决方案适用于一次只有一个连接的单线程,但我正在处理一个大型项目,其中事务和非事务都以不可预测的顺序发生。

最终,我需要的是一种关闭事务重试的方法,或者是一个修复程序,以使重试打开时事务不会崩溃。

我的 DbContext 有什么:

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

我的重试执行策略:

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

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

我的重试配置:

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);
        }
    }
}

除非我遗漏了什么,否则我相信我正确地实现了 Microsoft 的示例。根据我对他们示例的理解,EF应该为每个 repo 调用创建一个新的 RetryConfiguration 实例。否则,他们的示例没有任何意义,因为它只会创建与 RetryExecutionStrategy 而不是 DefaultExecutionStrategy 的连接,而 DefaultExecutionStrategy 是用户发起的事务所必需的。与此相反,我在 RetryConfiguration 的构造函数上设置了一个断点,发现它只实例化了一次。

这导致了很多令人头疼的问题,并试图实现他们的第二个解决方法,如下所示:

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;

我什至尝试手动调用 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();
    }
});

即使在那种情况下,我也会收到一个异常,说 RetryConfiguration 不允许用户启动的事务。

对于它的价值,我尝试将 RetryConfiguration.SuspendExecutionStrategy = true/false 添加到 TransactableRepository 类的 BeginTransaction()、Commit() 和 Rollback() 函数中。类本身只是连接的包装器。当它不起作用时我并不感到惊讶,因为微软的示例只显示它是从 RetryConfiguration 的构造函数中读取的,但我认为值得一试,因为我对 CallContext 不是很熟悉。

标签: c#transactionsentity-framework-6postgresql-9.4devart

解决方案


推荐阅读