【问题标题】:WCF transaction rollback while using two services to update two different databases - one being SQL data and other being Couchbase dataWCF 事务回滚,同时使用两个服务更新两个不同的数据库 - 一个是 SQL 数据,另一个是 Couchbase 数据
【发布时间】:2018-05-22 03:58:11
【问题描述】:

当我使用两个不同的服务来更新两个不同的数据库——一个是 SQL 数据,另一个是 Couchbase 数据时,如何使用 WCF 服务实现回滚数据库更新?

代码如下:

            My web.config looks like this:
            Web.Config:

            <system.serviceModel>
                <diagnostics>
                  <messageLogging logMalformedMessages="true" logMessagesAtTransportLevel="true" />
                </diagnostics>
                <behaviors>
                  <serviceBehaviors>
                    <behavior name="TransactionBehavior">
                      <!-- To avoid disclosing metadata information, 
                      set the values below to false before deployment -->
                      <!--<serviceMetadata httpGetEnabled="True" httpsGetEnabled="True" />-->
                      <serviceMetadata httpGetEnabled="True" />
                      <!-- To receive exception details in faults for debugging purposes, 
                      set the value below to true.  Set to false before deployment 
                      to avoid disclosing exception information -->
                      <serviceDebug includeExceptionDetailInFaults="True" />
                    </behavior>
                  </serviceBehaviors>
                  <endpointBehaviors>
                    <behavior name="ImpersonationBehavior">
                      <clientCredentials>
                        <windows allowedImpersonationLevel="Impersonation" />
                      </clientCredentials>
                    </behavior>
                  </endpointBehaviors>
                </behaviors>

                <bindings>
                  <wsHttpBinding>
                    <binding name="wsHttpTransactionBinding" receiveTimeout="00:20:00" sendTimeout="00:20:00" bypassProxyOnLocal="false" transactionFlow="true" maxReceivedMessageSize="2147483647" messageEncoding="Mtom">
                      <!--<reliableSession enabled="false" />-->
                      <security mode="None" />
                    </binding>
                  </wsHttpBinding>
                </bindings>

                <services>
                  <service behaviorConfiguration="TransactionBehavior" name="BankingService.AccountService">
                    <endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpTransactionBinding" name="wsHttpEndPoint" contract="SharedLib.IAccountService" />
                  </service>
                </services>
                <!--Fix could not activate service error-->
                <serviceHostingEnvironment minFreeMemoryPercentageToActivateService="1" multipleSiteBindingsEnabled="true">
                  <!--<serviceHostingEnvironment>-->
                  <serviceActivations>
                    <add relativeAddress="AccountService.svc" service="BankingService.AccountService" factory="WebApp.UnityServiceHostFactory, WebApp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
                  </serviceActivations>
                </serviceHostingEnvironment>
                <client>
                  <endpoint address="http://localhost:9003/AccountService.svc" behaviorConfiguration="ImpersonationBehavior" binding="wsHttpBinding" bindingConfiguration="wsHttpTransactionBinding" contract="SharedLib.IAccountService" name="wsHttpEndPoint" kind="" endpointConfiguration="" />
                </client>
             </system.serviceModel>


            App.config:

            <system.serviceModel>
                <bindings>
                  <wsHttpBinding>
                    <binding name="wsHttpTransactionBinding" transactionFlow="true" openTimeout="00:10:00" useDefaultWebProxy="true" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" receiveTimeout="00:20:00" sendTimeout="00:20:00" maxReceivedMessageSize="2147483647" messageEncoding="Mtom">
                       <security mode="None">
                      </security>
                    </binding>
                  </wsHttpBinding>
                  <netTcpBinding>
                    <binding name="netTcpTransactionBinding" transactionFlow="true" sendTimeout="00:20:00" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
                      <security mode="Transport">
                        <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
                      </security>
                    </binding>
                  </netTcpBinding>
                </bindings>
                <behaviors>
                  <endpointBehaviors>
                    <behavior name="ImpersonationBehavior">
                      <clientCredentials>
                        <windows allowedImpersonationLevel="Impersonation" />
                      </clientCredentials>
                    </behavior>
                  </endpointBehaviors>
                </behaviors>
                <client>
                  <endpoint address="http://localhost:9003/AccountService.svc"
                    behaviorConfiguration="ImpersonationBehavior" binding="wsHttpBinding"
                    bindingConfiguration="wsHttpTransactionBinding" contract="SharedLib.IAccountService"
                    name="wsHttpEndPoint" kind="" endpointConfiguration="" />
                </client>
            </system.serviceModel>

            ServiceContract is as below:
            IAccountService.cs

            [ServiceContract]    
            public interface IAccountService
            {
                [OperationContract]
                Task<IEnumerable<Account>> GetAccounts();

                [OperationContract, TransactionFlow(TransactionFlowOption.Allowed)]
                Task<bool> Debit(int accountID, int amount);

                [OperationContract, TransactionFlow(TransactionFlowOption.Allowed)]
                Task<bool> Credit(int accountID, int amount);

                [OperationContract]
                Task<int> GetBalance(int accountID);

                [OperationContract]
                Task<MiniStatement> GetMiniStatement(int accountID);
            }

            Service Implementation is as below:
            Account.cs

            [ServiceBehavior(
            TransactionIsolationLevel =
            System.Transactions.IsolationLevel.ReadCommitted,ConcurrencyMode = ConcurrencyMode.Multiple, ReleaseServiceInstanceOnTransactionComplete = false)]   
            public class AccountService : IAccountService
            {
                BankRepository bankRepository = new BankRepository();
                CouchbaseRepository couchbaseRepository = new CouchbaseRepository();

                public AccountService()
                {
                }

                [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
                public Task<bool> Credit(int accountID, int amount)
                {
                    var creditAccountTask = Task.Run(async () =>
                       {
                           return await bankRepository.CreditAccount(accountID, amount, Transaction.Current);
                       });
                    var result = creditAccountTask.ContinueWith(async (t) =>
                      {
                          return t.Result != null ? await couchbaseRepository.CreditAccount(accountID, t.Result) : false;
                      }, TaskContinuationOptions.OnlyOnRanToCompletion);
                    return result.Result;
                }

                [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
                public Task<bool> Debit(int accountID, int amount)
                {
                    var debitAccountTask = Task.Run(async () =>
                    {
                        return await bankRepository.DebitAccount(accountID, amount, Transaction.Current);
                    });
                    var result = debitAccountTask.ContinueWith(async (t) =>
                        {
                            return t.Result != null ? await couchbaseRepository.DebitAccount(accountID, t.Result) : false;
                        }, TaskContinuationOptions.OnlyOnRanToCompletion);
                    return result.Result;
                }

                public Task<IEnumerable<Account>> GetAccounts()
                {
                    return bankRepository.GetAccounts();
                }

                public Task<int> GetBalance(int accountID)
                {
                    return bankRepository.GetBalanceByAccountID(accountID);
                }

                public Task<MiniStatement> GetMiniStatement(int accountID)
                {
                    return couchbaseRepository.GetTransactionStatement(accountID);
                }

            }

当我使用 EF 保存更改时,事务仍然不起作用。 Transaction.Current 仍然为 null。

【问题讨论】:

  • 你需要一个分布式事务协调器
  • 我用过。相关问题是我在哪里启用 MSDTC?我有一个客户端,服务托管在另一个盒子中,SQL 服务器和 couchbase 数据库托管在不同的服务器中。我绝对不能在客户端机器上启用 MSDTC,因为我们的是桌面应用程序并且我们使用 ClickOnce 部署。

标签: sql wcf transactions couchbase rollback


【解决方案1】:
  1. 在您的服务绑定中启用事务流。
  2. 在您的操作合同上启用允许交易流。
  3. 从客户端创建事务并在同一个事务中调用这两个服务。

查看此blog post 解释事务以及如何启用它们。希望你能找到答案。

【讨论】:

  • 问题是因为我使用的是实体框架,它会创建自己的事务,因此我在第一个操作中所做的任何更新都会被提交。因此,即使第二次更新操作失败并且我回滚,初始更改也不会回滚。但是,如果我尝试通过执行 SQL 查询而不是 EF 来执行相同的操作,则回滚会起作用。
  • 您究竟是如何创建 EF 事务的?如果您没有在 EF 上明确创建事务,那么它应该已经加入您现有的事务。除非您在创建事务时启用异步流选项,否则异步函数也不会继承现有事务。
  • 我从客户端创建了一个转换范围,并从中调用我的服务,这些服务又调用存储库类来更新数据库。由于我使用 EF,因此事务可以毫不费力地提交。但是当我从我的客户处回滚时,它不起作用。但是,如果我将 EF 与类似这样的查询一起使用 - context.Database.ExecuteSqlCommandAsync(TransactionalBehavior.DoNotEnsureTransaction, sqlQuery);回滚工作正常
  • 似乎您的存储库类正在为数据库操作创建新的独立事务。在没有看到实际代码的情况下,我只能建议您确保您的 EF 操作是在与客户端或服务相同的事务上执行的。
  • 下面是示例代码,非常简单(基本): var account = await context.Account.FirstOrDefaultAsync(x => x.AccountID == accountID); account.Balance +=金额; context.Entry(account).State = EntityState.Modified;等待上下文.SaveChangesAsync();似乎 EF 创建了自己的事务,不知道为什么当我使用 EF 使用这样的查询时它不这样做: context.Database.ExecuteSqlCommandAsync(TransactionalBehavior.DoNotEnsureTransaction, sqlQuery);
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-04
相关资源
最近更新 更多