How to Create WCF Transaction
This article explains about the how to create a WCF service with transaction enabled. Refer “Transaction Mode” article to learn more about the server side and client side transaction mode.
WCF transaction was explained with below employee service
- Create the Employee WCF service to allow client user to insert employee detail
- Enable the transaction from server side by setting proper attributes
- Create the client application by consuming the Employee Service and insert the employee details
- Run the client application to successfully insert the employee detail
- Modify the service application to throw exception explicitly after successfully insert statement execution and check the transaction behavior.
Step 1:Create the Employee service that allows the addition of new employee details in DB. Decorate the operation contract with TransactionFlow attribute for enabling the transaction. TransactionFlowOption take three set of values.
- TransactionFlowOption.Allowed
- TransactionFlowOption.Mandatory
- TransactionFlowOption.NotAllowed
[ServiceContract]
public interface IService
{
[OperationContract]
[TransactionFlow(TransactionFlowOption.Allowed )]
bool AddEmployee(int id, string name, int salary);
}
Step 2:Create the service class which implements the service contract and set the operation behavior with TransactionScopeRequired = true . This attribute is used to enable the service transaction when the client transaction is not available.
public class Service : IService
{
[OperationBehavior(TransactionScopeRequired = true)]
public bool AddEmployee(int id, string name, int salary)
{
try
{
//Insert the employee tables inside the transaction
SqlConnection conn = new SqlConnection(@"Data Source=.\eaudit;Initial
Catalog=Test01;Integrated Security=SSPI;");
SqlCommand cmd = new SqlCommand("INSERT INTO [Test01].[dbo].[Employee]
VALUES("+id.ToString ()+",'"+name +"',
"+salary.ToString ()+")", conn);
cmd.CommandType = System.Data.CommandType.Text;
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
return true;
}
catch(Exception ex)
{
//return false ;
throw new FaultException(ex.Message);
}
}
}
Step 3:Update the service endpoint to enable transactions for wsHttpBinding by setting the transactionFlow attribute to true. Setting transactionFlow at config level doesn’t mean that the service wants to use the client’s transaction in every operation. It is required to set the transaction at the service contract level as mention in Step 1.
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
<services>
<service name="Service">
<endpoint address="" binding="wsHttpBinding"
contract="IService" bindingConfiguration="myTransactionBinding"/>
</service>
</services>
<bindings>
<wsHttpBinding>
<binding name="myTransactionBinding" transactionFlow="true" ></binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
Step 4:Now service creation is completed and let’s starts with the client application. Create a new console application from add Employee service as ServiceReference
Step 5:Create a new proxy object for the employee service and call the AddEmployee method.
static void Main(string[] args)
{
bool result=false ;
using (TransactionScope ts = new TransactionScope
(TransactionScopeOption.RequiresNew))
{
try
{
EmployeeService.ServiceClient service =
new EmployeeService.ServiceClient();
result = service.AddEmployee(1, "raj", 3000);
ts.Complete();
}
catch (Exception ex)
{
ts.Dispose();
Console.WriteLine(ex.Message);
}
}
if( result == true )
Console.WriteLine("Employee details add successfully");
else
Console.WriteLine ("Error while adding employee details");
Console.ReadLine();
}
Output:
Output shows the employee detail is added successfully and DB also shows the new entry is made
Step 6:Now everything works fine, let’s test the transaction by throwing the exception from server side after successful execution of employee details insert statement
try
{
//Insert the employee tables inside the transaction
SqlConnection conn = new SqlConnection(@"Data Source=.\eaudit;
Initial Catalog=Test01;Integrated Security=SSPI;");
SqlCommand cmd = new SqlCommand("INSERT INTO [Test01].[dbo].[Employee]
VALUES("+id.ToString ()+",'"+name +"',"+salary.ToString ()+")", conn);
cmd.CommandType = System.Data.CommandType.Text;
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
//Throw Exception after successful insert statement execution
throw new Exception("Sample exeception for testing");
return true;
}
catch(Exception ex)
{
//return false ;
throw new FaultException(ex.Message);
}
Step 7:Run the client application and check the output. Result clearly says that even insert statement is executed successfully and error is thrown after insert statement, DB is not updated. Because all the code execution is comes under the transaction so failure in any module of code will revert back all code execution.
Tips!
- Always create the service with Interface->Implementation format, mention the contract in Interface.
- Define the service in Class library and refer the class library in Host project. Don’t use service class in host project.
- Change the instance mode to per call as default.
- Always catch exception using try/catch block and throw exception using FaultException < T >.
- Logging and Include exception should be enable while compiling the project in debug mode. While in production deployment disable the logging and Include exception details.
|