WCF服务编程读书笔记(7):事务

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();
            }
        }
    }
}
View Code

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);
        }
    }
}
View Code

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();
                            }
                        }
                    }
                }
            }
        }
    }
}
View Code

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);

        }
    }
}
View Code

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);

        }
    }
}
View Code

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);

        }
    }
}
View Code

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();
            }

        }
    }
}
View Code

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;
            }
        }
    }
}
View Code

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();
            }
        }
    }
}
View Code

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();
            }
        }
    }
}
View Code

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();
            }
        }
    }
}
View Code

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();
            }
        }
    }
}
View Code

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
            { }
        }
    }
}
View Code

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()
        {
        }
    }
}
View Code

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();
            }
        }
    }
}
View Code

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()
        { }
    }
}
View Code

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()
        {
        }
    }
}
View Code

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()
        {
        }
    }
}
View Code

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()
        {
        }
    }
}
View Code

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();
            }
        }
    }
}
View Code

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()
        {
        }
    }
}
View Code

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()
        {
        }
    }
}
View Code

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
        }
    }
}
View Code

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();
        }
    }
}
View Code

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);
        }
    }
}
View Code

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();
            }
        }
    }
}
View Code

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);
        }
    }
}
View Code

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);
        }
    }
}
View Code

//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();
        }
    }
}
View Code

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();
        }
    }
}
View Code
原文地址:https://www.cnblogs.com/thlzhf/p/2782990.html