How to handle transactions in WCF services

I have a WPF application, and in a button click event, I call the WCF service to process the transaction. After completing one transaction, I throw an exception. To see if the transaction will be rolled back or not. But even I received an error, the transaction was still completed, it was not interrupted in the database.

UI Button Event:

private void button1_Click(object sender, RoutedEventArgs e) { binding = new WSHttpBinding(); endpoint = new EndpointAddress("http://localhost:6144/EmployeeService.svc/EmployeeService"); ChannelFactory<IEmployeeService> cf = new ChannelFactory<IEmployeeService>(binding, endpoint); IEmployeeService obj = cf.CreateChannel(); using (TransactionScope ts = new TransactionScope(TransactionScopeOption.RequiresNew)) { try { obj.UpdateProductData(201, 10); throw new Exception("Wrong"); obj.UpdateProductData(202, 15); ts.Complete(); } catch (Exception ex) { ts.Dispose(); } } } 

App.config already says "transactionFlow="true" .

WCF Service:

 [OperationBehavior(TransactionScopeRequired=true,TransactionAutoComplete=true)] public bool UpdateProductData(int ProdId, int Amount) { DALClass objDALProd = new DALClass(); return objDALProd.UpdateProductData(ProdId, Amount); } 

DALClass:

 public bool UpdateProductData(int prodID,int Amount) { try { objComd.Connection = objConn; objConn.Open(); objComd.CommandText = "UpdateEmployeeData"; objComd.CommandType = CommandType.StoredProcedure; objParam = new SqlParameter("@ProductId", prodID); objComd.Parameters.Add(objParam); objParam = new SqlParameter("@Amount", Amount); objComd.Parameters.Add(objParam); objComd.ExecuteNonQuery(); objConn.Close(); } catch(Exception ex) { throw new FaultException("Database Error"); } return true; } 

Please let me know where my mistake is. A transaction does not roll back, even when I raise an exception, the first transaction does not roll back.

+4
source share
3 answers

Have you identified the transaction flow for your WCF contract?

 [OperationContract] [TransactionFlow(TransactionFlowOption.Mandatory)] 

Have you determined the transaction flow for both client configuration and server configuration?

 <wsHttpBinding> <binding name="transactionalWsatHttpBinding" transactionFlow="true" /> </wsHttpBinding> 
+2
source
  • You need to enable transactions for both client binding and service binding by setting WSHttpBindingBase.TransactionFlow = true.

  • You can check the called service if the transaction is actually proceeding by checking that Transaction.Current.TransactionInformation.DistributedIdentifier is nonzero. If it is zero, then the thread does not work, and rollback will not be performed.

See the answer. How can I implement WCF transaction support in a custom class using CoreService? for more information and links.

+2
source

Your client-side code looks good to me, but on the server side there are only 3 attributes needed to work.

The service interface requires a [TransactionFlow] operation.

 [ServiceContract] public interface IService { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] bool UpdateProductData(int ProdId, int Amount); } 

A service class requires a [ServiceBehavior] class and [OperationBehavior] by the method:

 [ServiceBehavior(TransactionIsolationLevel = IsolationLevel.ReadUncommitted)] public class Service : IService { [OperationBehavior(TransactionScopeRequired=true,TransactionAutoComplete=true)] public bool UpdateProductData(int ProdId, int Amount) { DALClass objDALProd = new DALClass(); return objDALProd.UpdateProductData(ProdId, Amount); } } 

Also note that these attributes are only one way to achieve this - you can also do all this in the configuration file if necessary. Configuration files make it easy to apply these settings to multiple services in one shot.

+1
source

Source: https://habr.com/ru/post/1434748/


All Articles