WCF Transactions
I am sure that transactions are not a new thing for you. For WCF Transactions specifically, let's think of a scenario of two tables in a database, Login and LoginTrans.
And in our service there are two functions, INSERT that takes one string parameter and inserts it into the LOGIN table and the UPDATE function that updates the LoginTrans table. The client will send his / her user id to the insert method and it will insert the user id into the login table and update the LoginTrans table. The problem however is the update code is written in a different function so the client will first call the insert method then the update method so the question becomes, how to maintain the atomicity, in other words either both function should execute and make changes to the database or neither. Let's see now how to use a WCF Transaction to deal with this. In the Service Contract use a TransactionFlow attribute on the functions that you want to run under the transaction, like this:
- [ServiceContract]
- public interface IAhmarService
- {
- [FaultContract(typeof(string))]
- [OperationContract]
- [TransactionFlow(TransactionFlowOption.Allowed)]
- void Insert(string UserId);
- [FaultContract(typeof(string))]
- [OperationContract]
- [TransactionFlow(TransactionFlowOption.Allowed)]
- string Update();
- }
- Allowed: Implies if client has transaction, it will be accepted otherwise not a problem.
- NotAllowed: Client cannot send transaction to server while calling a method.
- Mandatory: Client has to create a transaction otherwise the method cannot be called.
In the service class decorate the functions with[OperationBehavior(TransactionScopeRequired = true)]
It will look like:
- public class AhmarService : IAhmarService
- {
- [OperationBehavior(TransactionScopeRequired = true)]
- public void Insert(string uid)
- {
- try
- {
- string constring = @"Data Source=AHMAR-PC\SQLEXPRESS;Initial Catalog=DEVDB;Integrated Security=True;User ID=Ahmar-PC\Ahmar";
- SqlConnection con = new SqlConnection(constring);
- con.Open();
- SqlCommand cmd = new SqlCommand("insert into Login values(@uid)", con);
- cmd.Parameters.AddWithValue("@uid", uid);
- cmd.ExecuteNonQuery();
- }
- catch (Exception ex)
- {
- throw new FaultException("error");
- }
- }
- [OperationBehavior(TransactionScopeRequired = true)]
- public string Update()
- {
- try
- {
- string constring = @"Data Source=AHMAR-PC\SQLEXPRESS;Initial Catalog=DEVDB;Integrated Security=True;User ID=Ahmar-PC\Ahmar";
- SqlConnection con = new SqlConnection(constring);
- //throw new Exception("asa");
- con.Open();
- SqlCommand cmd = new SqlCommand("Update LoginTrans set Islogin='1'", con);
- cmd.ExecuteNonQuery();
- return "1";
- }
- catch (Exception ex)
- {
- throw new FaultException("error");
- }
- }
- }
- <bindings>
- <wsHttpBinding>
- <binding name="httpBin" ="" transactionFlow="true"/>
- </wsHttpBinding>
- </bindings>
- <?xml version="1.0"?>
- <configuration>
- <system.web>
- <compilation debug="true" targetFramework="4.0" />
- </system.web>
- <system.serviceModel>
- <bindings>
- <wsHttpBinding>
- <binding name="httpBin" ="" transactionFlow="true"/>
- </wsHttpBinding >
- </bindings>
- <services>
- <service name="WcfService2.AhmarService">
- <endpoint address="" behaviorConfiguration="webby" binding="wsHttpBinding" bindingConfiguration="httpBin"
- contract="WcfService2.IAhmarService" />
- <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
- </service>
- </services>
- <behaviors>
- <endpointBehaviors>
- <behavior name="webby" >
- <!--<webHttp helpEnabled="true"/>-->
- </behavior>
- </endpointBehaviors>
- <serviceBehaviors>
- <behavior>
- <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
- <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="false"/>
- </behavior>
- </serviceBehaviors>
- </behaviors>
- <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
- </system.serviceModel>
- <system.webServer>
- <modules runAllManagedModulesForAllRequests="true"/>
- </system.webServer>
- </configuration>
That will generate an app.config and Proxy class. If you want to generate it using SVCUTIL.EXE then you can do that.
Add a namespace as "using System.Transactions",the code will look like this:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Transactions;
- using System.ServiceModel;
- namespace ConsoleApplication1
- {
- class Program
- {
- static void Main(string[] args)
- {
- using (TransactionScope ts = new TransactionScope())
- {
- try
- {
- ServiceReference1.AhmarServiceClient sc = new ServiceReference1.AhmarServiceClient();
- sc.Insert("ahmar");
- sc.Update();
- ts.Complete();
- }
- catch (FaultException ex)
- {
- ts.Dispose();
- Console.WriteLine("Data RollBacked error in service");
- }
- catch (Exception ex)
- {
- }
- }
- }
- }
- }

Ahmar HusainPosted Feb 17, 2014, 5:58 AM
Thank you RV
Rv SinghPosted Jan 17, 2014, 1:50 AM
nice article