Example 7-1. Explicit transaction management
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; using System.Diagnostics; using System.Runtime.Serialization; using System.ServiceModel.Dispatcher; using System.ServiceModel.Description; using System.Collections.ObjectModel; using System.Data; using System.Data.SqlClient; namespace WCFServiceProgramming.Library { [ServiceContract] public interface IMyContract { [OperationContract] void MyMethod(); } class MyService : IMyContract { public void MyMethod() { //Avoid this programming model string connectionString = ""; IDbConnection conn = new SqlConnection(connectionString); conn.Open(); IDbCommand comm = new SqlCommand(); comm.Connection = conn; IDbTransaction trans = conn.BeginTransaction(); //Enlisting comm.Transaction = trans; try { // Interact with database here, then commit the transaction trans.Commit(); } catch { trans.Rollback(); // Abort transaction } finally { conn.Close(); comm.Dispose(); trans.Dispose(); } } } }
Example 7-2. Configuring for the Client/Service transaction mode
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Diagnostics; using System.Transactions; namespace WCFServiceProgramming.Library { [ServiceContract] public interface IMyContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void MyMethod(); } class MyService : IMyContract { [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod() { Transaction transaction = Transaction.Current; Debug.Assert(transaction != null); } } }
Example 7-3. The BindingRequirementAttribute
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel.Description; using System.ServiceModel; using System.Collections.ObjectModel; using System.ServiceModel.Channels; namespace WCFServiceProgramming.Library { class BindingRequirementAttribute : Attribute, IServiceBehavior { public bool TransactionFlowEnabled { get; set; } public void AddBindingParameters(ServiceDescription description, ServiceHostBase host, Collection<ServiceEndpoint> endpoints, BindingParameterCollection parameters) { } public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase host) { } public void Validate(ServiceDescription description, ServiceHostBase host) { if (TransactionFlowEnabled == false) { return; } foreach (ServiceEndpoint endpoint in description.Endpoints) { Exception exception = new InvalidOperationException(); foreach (OperationDescription operation in endpoint.Contract.Operations) { foreach (IOperationBehavior behavior in operation.Behaviors) { if (behavior is TransactionFlowAttribute) { TransactionFlowAttribute attribute = behavior as TransactionFlowAttribute; if (attribute.Transactions == TransactionFlowOption.Allowed) { if (endpoint.Binding is NetTcpBinding) { NetTcpBinding tcpBinding = endpoint.Binding as NetTcpBinding; if (tcpBinding.TransactionFlow == false) { throw exception; } break; } // Similar checks for the rest of the transaction-aware bindings throw new InvalidOperationException(); } } } } } } } }
Example 7-4. Configuring for the Client transaction mode
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Diagnostics; using System.Transactions; namespace WCFServiceProgramming.Library { [ServiceContract] public interface IMyContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Mandatory)] void MyMethod(); } class MyService : IMyContract { [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod() { Transaction transaction = Transaction.Current; Debug.Assert(transaction.TransactionInformation.DistributedIdentifier != Guid.Empty); } } }
Example 7-5. Configuring for the Service transaction mode
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Diagnostics; using System.Transactions; namespace WCFServiceProgramming.Library { [ServiceContract] public interface IMyContract { [OperationContract] void MyMethod(); } class MyService : IMyContract { [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod() { Transaction transaction = Transaction.Current; Debug.Assert(transaction.TransactionInformation.DistributedIdentifier == Guid.Empty); } } }
Example 7-6. Configuring for the None transaction mode
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Diagnostics; using System.Transactions; namespace WCFServiceProgramming.Library { [ServiceContract] public interface IMyContract { [OperationContract] void MyMethod(); } class MyService : IMyContract { public void MyMethod() { Transaction transaction = Transaction.Current; Debug.Assert(transaction == null); } } }
Example 7-7. Using TransactionScope
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Diagnostics; using System.Transactions; namespace WCFServiceProgramming.Library { [ServiceContract] public interface IMyContract { [OperationContract] void MyMethod(); } class MyService : IMyContract { public void MyMethod() { using (TransactionScope scope = new TransactionScope()) { // Perform transaction work here // No errors - commit transaction scope.Complete(); } } } }
Example 7-8. TransactionScope and error handling
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Diagnostics; using System.Transactions; namespace WCFServiceProgramming.Library { [ServiceContract] public interface IMyContract { [OperationContract] void MyMethod(); } class MyService : IMyContract { public void MyMethod() { try { using (TransactionScope scope = new TransactionScope()) { // Perform transaction work here // No errors - commit transaction scope.Complete(); } } catch (TransactionAbortedException ex) { Trace.WriteLine(ex.Message); } catch (Exception ex) // Any other exception took place { Trace.WriteLine("Cannot complete transaction"); throw ex; } } } }
Example 7-9. Direct scope nesting
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Diagnostics; using System.Transactions; namespace WCFServiceProgramming.Library { [ServiceContract] public interface IMyContract { [OperationContract] void MyMethod(); } class MyService : IMyContract { public void MyMethod() { using (TransactionScope scope1 = new TransactionScope()) { using (TransactionScope scope2 = new TransactionScope()) { scope2.Complete(); } scope1.Complete(); } } } }
Example 7-10. Indirect scope nesting
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Diagnostics; using System.Transactions; namespace WCFServiceProgramming.Library { [ServiceContract] public interface IMyContract { [OperationContract] void MyMethod(); } class MyService : IMyContract { public void MyMethod() { using (TransactionScope scope = new TransactionScope()) { // Perform transaction work here SomeMethod(); scope.Complete(); } } private static void SomeMethod() { using (TransactionScope scope = new TransactionScope()) { // Perform transaction work here scope.Complete(); } } } }
Example 7-11. Scope nesting inside a service method
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Diagnostics; using System.Transactions; namespace WCFServiceProgramming.Library { [ServiceContract] public interface IMyContract { [OperationContract] void MyMethod(); } class MyService : IMyContract { [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod() { using (TransactionScope scope = new TransactionScope()) { // Perform transaction work here scope.Complete(); } } } }
Example 7-12. Using TransactionScopeOption.Required in a downstream class
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Diagnostics; using System.Transactions; namespace WCFServiceProgramming.Library { [ServiceContract] public interface IMyContract { [OperationContract] void MyMethod(); } class MyService : IMyContract { [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod() { MyClass obj = new MyClass(); obj.SomeMethod(); } } class MyClass { public void SomeMethod() { using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required)) { // Do some work then scope.Complete(); } } } }
Example 7-13. Using TransactionScopeOption.Suppress
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Diagnostics; using System.Transactions; namespace WCFServiceProgramming.Library { [ServiceContract] public interface IMyContract { [OperationContract] void MyMethod(); } class MyService : IMyContract { [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod() { try { // Start of nontransactional section using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Suppress)) { // Do nontransactional work here } // Restores ambient transaction here } catch { } } } }
Example 7-14. Using TransactionScope to call services in a single transaction(Service Side)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Diagnostics; using System.Transactions; namespace WCFServiceProgramming.Library { /////////////////////////////////// Service Side ////////////////////////////////////// [ServiceContract] public interface IMyContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void MyMethod(); } [ServiceContract] public interface IMyOtherContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Mandatory)] void MyOtherMethod(); } class MyService : IMyContract { [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod() { } } class MyOtherService : IMyOtherContract { [OperationBehavior(TransactionScopeRequired = true)] public void MyOtherMethod() { } } }
Example 7-14. Using TransactionScope to call services in a single transaction(Client Side)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using WCFServiceProgramming.Library; using System.Transactions; namespace WCFServiceProgramming.Client { ////////////////////////////////// Client Side ///////////////////////////////// class MyContractClient : ClientBase<IMyContract>, IMyContract { public void MyMethod() { Channel.MyMethod(); } } class MyOtherContractClient : ClientBase<IMyOtherContract>, IMyOtherContract { public void MyOtherMethod() { Channel.MyOtherMethod(); } } class Program { static void Main(string[] args) { using (TransactionScope scope = new TransactionScope()) { MyContractClient proxy1 = new MyContractClient(); proxy1.MyMethod(); proxy1.Close(); MyOtherContractClient proxy2 = new MyOtherContractClient(); proxy2.MyOtherMethod(); proxy2.Close(); scope.Complete(); } using (MyContractClient proxy3 = new MyContractClient()) using (MyOtherContractClient proxy4 = new MyOtherContractClient()) using (TransactionScope scope = new TransactionScope()) { proxy3.MyMethod(); proxy4.MyOtherMethod(); scope.Complete(); } } } }
Example 7-15. Implementing a transactional service
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Transactions; using System.Runtime.Serialization; namespace WCFServiceProgramming.Library { [DataContract] class Param { } [ServiceContract] public interface IMyContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void MyMethod(); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] class MyService : IMyContract, IDisposable { [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod(Param stateIdentifier) { GetState(stateIdentifier); DoWork(); SaveState(stateIdentifier); } void GetState(Param stateIdentifier) { } void DoWork() { } void SaveState(Param stateIdentifier) { } public void Dispose() { } } }
Example 7-16. Per-session yet per-call transactional service
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Transactions; using System.Runtime.Serialization; namespace WCFServiceProgramming.Library { [ServiceContract(SessionMode = SessionMode.Required)] public interface IMyContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void MyMethod(); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] class MyService : IMyContract { [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod() { } } }
Example 7-17. Per-session transactional service
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Transactions; using System.Runtime.Serialization; namespace WCFServiceProgramming.Library { [ServiceContract(SessionMode = SessionMode.Required)] public interface IMyContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void MyMethod(); } [ServiceBehavior(ReleaseServiceInstanceOnTransactionComplete = false)] class MyService : IMyContract { [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod() { } } }
Example 7-18. State-aware, transactional per-session service
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Transactions; using System.Runtime.Serialization; namespace WCFServiceProgramming.Library { [ServiceContract(SessionMode = SessionMode.Required)] public interface IMyContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void MyMethod(); } [ServiceBehavior(ReleaseServiceInstanceOnTransactionComplete = false)] class MyService : IMyContract, IDisposable { readonly string _stateIdentifier; public MyService() { InitializeState(); _stateIdentifier = OperationContext.Current.SessionId; SaveState(); } [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod() { GetState(); Dowork(); SaveState(); } public void Dispose() { RemoveState(); } // Helper methods private void InitializeState() { } private void GetState() { } private void Dowork() { } private void SaveState() { } private void RemoveState() { } } }
Example 7-19. Using volatile resource managers to achieve stateful per-session transactional service
Example 7-20. Launching concurrent transactions
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using WCFServiceProgramming.Library; using System.Transactions; namespace WCFServiceProgramming.Client { class MyContractClient : ClientBase<IMyContract>, IMyContract { public void MyMethod() { Channel.MyMethod(); } } class Program { static void Main(string[] args) { using (TransactionScope scope1 = new TransactionScope()) { MyContractClient proxy = new MyContractClient(); proxy.MyMethod(); using (TransactionScope scope2 = new TransactionScope(TransactionScopeOption.RequiresNew)) { proxy.MyMethod(); scope2.Complete(); } proxy.Close(); scope1.Complete(); } } } }
Example 7-21. Setting TransactionAutoComplete to false
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Transactions; using System.Runtime.Serialization; namespace WCFServiceProgramming.Library { [ServiceContract(SessionMode = SessionMode.Required)] public interface IMyContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void MyMethod1(); [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void MyMethod2(); } class MyService : IMyContract { [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)] public void MyMethod1() { } [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)] public void MyMethod2() { } } }
Example 7-22. Hybrid per-session service
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Transactions; using System.Runtime.Serialization; namespace WCFServiceProgramming.Library { [ServiceContract(SessionMode = SessionMode.Required)] public interface IMyContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void MyMethod1(); [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void MyMethod2(); } [ServiceBehavior(TransactionAutoCompleteOnSessionClose = true)] class MyService : IMyContract { [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)] public void MyMethod1() { } [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)] public void MyMethod2() { } } }
Example 7-23. State-aware singleton(Service Side)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Transactions; using System.Runtime.Serialization; namespace WCFServiceProgramming.Library { [ServiceContract(SessionMode = SessionMode.Required)] public interface IMyContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void MyMethod(); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)] class MyService : IMyContract { readonly static string _stateIdentifier = typeof(MyService).GUID.ToString(); [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod() { GetState(); Dowork(); SaveState(); } private void GetState() { } private void Dowork() { } public void SaveState() { // Use _stateIdentifier to save state } public void RemoveState() { // Use _stateIdentifier to remove the state from the resource manager } } }
Example 7-23. State-aware singleton(Hosting)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using WCFServiceProgramming.Library; using System.ServiceModel; using System.ServiceModel.Description; namespace WCFServiceProgramming.Host { class Program { static void Main(string[] args) { MyService singleton = new MyService(); singleton.SaveState(); ServiceHost host = new ServiceHost(singleton); host.Open(); // Some blocking calls host.Close(); singleton.RemoveState(); } } }
Example 7-24. Achieving stateful singleton transactional service (Service Side)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Transactions; using System.Runtime.Serialization; using System.Diagnostics; namespace WCFServiceProgramming.Library { //////////////////////////////// Service Side ///////////////////////////////// [ServiceContract(SessionMode = SessionMode.Required)] public interface IMyContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void MyMethod(); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ReleaseServiceInstanceOnTransactionComplete = false)] public class MyService : IMyContract { int _counter = 0; [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod() { _counter++; Trace.WriteLine("Counter: " + _counter); } } }
Example 7-24. Achieving stateful singleton transactional service (Client Side)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using WCFServiceProgramming.Library; using System.Transactions; namespace WCFServiceProgramming.Client { ////////////////////////////// Client Side ///////////////////////////////// class MyContractClient : ClientBase<IMyContract>, IMyContract { public void MyMethod() { Channel.MyMethod(); } } class Program { static void Main(string[] args) { using (TransactionScope scope1 = new TransactionScope()) { MyContractClient proxy = new MyContractClient(); proxy.MyMethod(); proxy.Close(); scope1.Complete(); } using (TransactionScope scope2 = new TransactionScope()) { MyContractClient proxy = new MyContractClient(); proxy.MyMethod(); proxy.Close(); scope2.Complete(); } using (TransactionScope scope3 = new TransactionScope()) { MyContractClient proxy = new MyContractClient(); proxy.MyMethod(); proxy.Close(); scope3.Complete(); } } } }
Example 7-25. Configuring the callback for Service transaction
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Transactions; using System.Diagnostics; namespace WCFServiceProgramming.Library { public interface IMyContractCallback { [OperationContract] [TransactionFlow(TransactionFlowOption.Mandatory)] void OnCallback(); } class MyClient : IMyContractCallback { [OperationBehavior(TransactionScopeRequired = true)] public void OnCallback() { Transaction transaction = Transaction.Current; Debug.Assert(transaction.TransactionInformation. DistributedIdentifier != Guid.Empty); } } }
Example 7-26. Out-of-band callbacks
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Transactions; using System.Runtime.Serialization; using System.Diagnostics; namespace WCFServiceProgramming.Library { [ServiceContract(SessionMode = SessionMode.Required)] public interface IMyContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void MyMethod(); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] public class MyService : IMyContract { static List<IMyContractCallback> _callbacks = new List<IMyContractCallback>(); public void MyMethod() { IMyContractCallback callback = OperationContext.Current.GetCallbackChannel<IMyContractCallback>(); if (_callbacks.Contains(callback) == false) { _callbacks.Add(callback); } } public static void CallClients() { Action<IMyContractCallback> invoke = delegate(IMyContractCallback callback) { using (TransactionScope scope = new TransactionScope()) { callback.OnCallback(); scope.Complete(); } }; _callbacks.ForEach(invoke); } } }
//Out-of-band callbacks:
MyService.CallClients( );
Example 7-27. Configuring for transactional callbacks(Service Side)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Transactions; using System.Runtime.Serialization; using System.Diagnostics; namespace WCFServiceProgramming.Library { [ServiceContract(CallbackContract = typeof(IMyContractCallback))] public interface IMyContract { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void MyMethod(); } public interface IMyContractCallback { [OperationContract] [TransactionFlow(TransactionFlowOption.Allowed)] void OnCallback(); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Reentrant, ReleaseServiceInstanceOnTransactionComplete = false)] public class MyService : IMyContract { [OperationBehavior(TransactionScopeRequired = true)] public void MyMethod() { Trace.WriteLine("Service ID: " + Transaction.Current.TransactionInformation.DistributedIdentifier); IMyContractCallback callback = OperationContext.Current.GetCallbackChannel<IMyContractCallback>(); callback.OnCallback(); } } }
Example 7-27. Configuring for transactional callbacks(Client Side)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using WCFServiceProgramming.Library; using System.Transactions; using System.Diagnostics; namespace WCFServiceProgramming.Client { class MyClient : IMyContractCallback { [OperationBehavior(TransactionScopeRequired = true)] public void OnCallback() { Trace.WriteLine("OnCallback ID: " + Transaction.Current.TransactionInformation.DistributedIdentifier); } } class MyContractClient : ClientBase<IMyContract>, IMyContract { private InstanceContext _context; public MyContractClient(InstanceContext context) { this._context = context; } public void MyMethod() { Channel.MyMethod(); } } class Program { static void Main(string[] args) { MyClient client = new MyClient(); InstanceContext context = new InstanceContext(client); MyContractClient proxy = new MyContractClient(context); using (TransactionScope scope = new TransactionScope()) { proxy.MyMethod(); Trace.WriteLine("Client ID: " + Transaction.Current.TransactionInformation.DistributedIdentifier); scope.Complete(); } proxy.Close(); } } }