尝试创建/使用内部事务时出现异常

发布于 2024-10-07 19:06:56 字数 1828 浏览 2 评论 0原文

我的应用程序(WCF 服务)使用 LINQ 数据上下文,最近我们决定将所有内容包装在事务中。看起来效果不错;对服务的每个调用都有自己的事务,因此如果引发异常,则所有内容都会回滚,并且不会向数据库提交任何更改。

然而,我们有一个“主管代码”的概念,它只能生成和使用一次。我们希望即使稍后在操作中发生错误,主管代码也会被标记为正在使用。对我来说,这表明我应该使用内部交易?

因此,在我的 SetToUsed 方法中,我放入了一个新事务,如下所示:

    public void SetToUsed(string code)
    {
        // security codes explicitly need their own transaction so that they will still be marked as having been used even if the outer transaction were to be rolled back due to error
        using (var securityCodeTransaction = new TransactionScope(
                TransactionScopeOption.RequiresNew,
                new TransactionOptions
                {
                    IsolationLevel = IsolationLevel.ReadUncommitted
                },
                EnterpriseServicesInteropOption.Automatic))
        {
            var returnedItems = m_safetyCodeRepository.FindAll(sc => sc.safetyCode == code &&
                                                                     sc.safetyCodeTypeId == (long)GetCodeType() &&
                                                                     sc.isUsed == false);
            foreach (var item in returnedItems)
            {
                item.isUsed = true;
                m_safetyCodeRepository.SaveChanges(item);
            }

            securityCodeTransaction.Complete();
        }
    }

但是,这会导致异常: System.InvalidOperationException:连接当前已登记事务。完成当前事务并重试。
异常在 FindAll 行上抛出,这是一个薄包装器,用于表示

dataContext.GetTable<tbSftSafetyCodes>().Where(exp).ToList()

我是否遗漏了某些内容,或者以完全错误的方式处理此问题?

编辑:我意识到我实际上并不需要针对主管代码更改本身进行事务。
因此,我将 TransactionScopeOption 更改为 TransactionScopeOption.Suppress。我仍然想知道为什么使用 TransactionScopeOption.RequiresNew 进行内部事务不起作用!

My application (WCF service) uses LINQ data contexts and recently we've decided to wrap everything in a transaction. Seems to work pretty well; each call into the service has its own transaction so if an exception is thrown then everything is rolled back and no changes are committed to the database.

However we have a notion of a "supervisor code", which can be generated and used once-only. We wish that the supervisor code will be marked as being used even if an error should occur later in the operation. To me, this suggests that I should use an inner transaction?

So inside my SetToUsed method, I put in a new transaction like so:

    public void SetToUsed(string code)
    {
        // security codes explicitly need their own transaction so that they will still be marked as having been used even if the outer transaction were to be rolled back due to error
        using (var securityCodeTransaction = new TransactionScope(
                TransactionScopeOption.RequiresNew,
                new TransactionOptions
                {
                    IsolationLevel = IsolationLevel.ReadUncommitted
                },
                EnterpriseServicesInteropOption.Automatic))
        {
            var returnedItems = m_safetyCodeRepository.FindAll(sc => sc.safetyCode == code &&
                                                                     sc.safetyCodeTypeId == (long)GetCodeType() &&
                                                                     sc.isUsed == false);
            foreach (var item in returnedItems)
            {
                item.isUsed = true;
                m_safetyCodeRepository.SaveChanges(item);
            }

            securityCodeTransaction.Complete();
        }
    }

However, this causes an exception: System.InvalidOperationException: Connection currently has transaction enlisted. Finish current transaction and retry.
The exception is thrown on the FindAll line, which is a thin wrapper for

dataContext.GetTable<tbSftSafetyCodes>().Where(exp).ToList()

Am I missing something, or going about this in entirely the wrong way?

Edit: I realised that I did not actually need a Transaction for the Supervisor Code changes per se.
So I changed the TransactionScopeOption to be TransactionScopeOption.Suppress instead. I would still like to know why having the inner transaction using TransactionScopeOption.RequiresNew didn't work though!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

梦亿 2024-10-14 19:06:56
TransactionScopeOption.RequiresNew    

尝试启动一项独立于其所在交易的全新交易,这是您的意图吗? “需要”选项会将新事务登记到当前活动的事务中。

但是,如果 yopu 始终希望将代码标记为已使用 - 您是否需要将其包含在事务中?您能否在开始事务包装操作之前将其标记为已使用并提交更改?

TransactionScopeOption.RequiresNew    

attempts to start an entirely new transaction which is independent of the one it sits within, is this what you intend? The Requires option will enlist the new transaction into the currently active one.

However, if yopu always want the code to be marked as used - do you need to have it in a transaction at all? Could you not just mark it as used amd commit that change just before you start your transaction-wrapped operation?

流绪微梦 2024-10-14 19:06:56

就我而言,除了使用选项 TransactionScopeOption.RequiresNew 之外,还需要在 using 语句之前使用

    dc.Connection.Open();
    dc.Connection.EnlistTransaction(Transaction.Current);
    if (dc.Connection.State!=ConnectionState.Closed) dc.Connection.Close();

    using (var securityCodeTransaction = new TransactionScope(
            TransactionScopeOption.RequiresNew,
            new TransactionOptions
            {
                IsolationLevel = IsolationLevel.ReadUncommitted
            },
            EnterpriseServicesInteropOption.Automatic))
    {
            // same code as in question
    }

这使得问题消失 - 但是,我不知道为什么需要这样做。

注意:为了登记事务,连接必须打开 - 因此需要 open + close 语句。

In my case beside using the option TransactionScopeOption.RequiresNew it was also necessary to preceed the using statement with

    dc.Connection.Open();
    dc.Connection.EnlistTransaction(Transaction.Current);
    if (dc.Connection.State!=ConnectionState.Closed) dc.Connection.Close();

    using (var securityCodeTransaction = new TransactionScope(
            TransactionScopeOption.RequiresNew,
            new TransactionOptions
            {
                IsolationLevel = IsolationLevel.ReadUncommitted
            },
            EnterpriseServicesInteropOption.Automatic))
    {
            // same code as in question
    }

That made the issue go away - however, I have no idea why that was required.

Note: In order to enlist the transaction, the connection must be open - hence the open + close statements are required.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文