Example 5-1. Defining and configuring a callback contract
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; namespace WCFServiceProgramming.Library { public interface ISomeCallbackContract { [OperationContract] void OnCallback(); } [ServiceContract(CallbackContract = typeof(ISomeCallbackContract))] public interface IMyContract { [OperationContract] void DoSomething(); } }
Example 5-2. The DuplexClientBase<T> class
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; namespace WCFServiceProgramming { public interface IDuplexContextChannel : IContextChannel { InstanceContext CallbackInstance { get; set; } //... } public abstract class DuplexClientBase<T> : ClientBase<T> where T : class { protected DuplexClientBase(InstanceContext callbackContext); protected DuplexClientBase(InstanceContext callbackContext, string endpointName); protected DuplexClientBase(InstanceContext callbackContext, Binding binding, EndpointAddress remoteAddress); protected DuplexClientBase(object callbackInstance); protected DuplexClientBase(object callbackInstance, string endpointConfiguratinName); protected DuplexClientBase(object callbackInstance, Binding binding, EndpointAddress remoteAddress); public IDuplexContextChannel InnerDuplexChannel { get; } //... } }
Example 5-3. Tool-generated duplex proxy
using System; using System.Collections.Generic; using System.Linq; using System.Text; using WCFServiceProgramming.Library; using System.ServiceModel; using System.ServiceModel.Channels; namespace WCFServiceProgramming { class MyContractClient : DuplexClientBase<IMyContract>, IMyContract { public MyContractClient(InstanceContext callbackContext) : base(callbackContext) { } public MyContractClient(InstanceContext callbackContext, string endpointName) : base(callbackContext, endpointName) { } public MyContractClient(InstanceContext callbackContext, Binding binding, EndpointAddress remoteAddress) : base(callbackContext, binding, remoteAddress) { } public void DoSomething() { Channel.DoSomething(); } } }
Example 5-4. Client implementing the callback contract
using System; using System.Collections.Generic; using System.Linq; using System.Text; using WCFServiceProgramming.Library; using System.ServiceModel; namespace WCFServiceProgramming { class MyClient : IMyContractCallback, IDisposable { MyContractClient m_Proxy; public void CallService() { InstanceContext context = new InstanceContext(this); m_Proxy = new MyContractClient(context); m_Proxy.DoSomething(); } public void OnCallback() { //... } public void Dispose() { m_Proxy.Close(); } } }
Example 5-5. Using a reworked object-based proxy
using System; using System.Collections.Generic; using System.Linq; using System.Text; using WCFServiceProgramming.Library; using System.ServiceModel; namespace WCFServiceProgramming { class MyContractClient : DuplexClientBase<IMyContract>, IMyContract { public MyContractClient(object callbackInstance) : base(callbackInstance) { } public void DoSomething() { Channel.DoSomething(); } } class MyClient : IMyContractCallback, IDisposable { MyContractClient m_Proxy; public void CallService() { m_Proxy = new MyContractClient(this); m_Proxy.DoSomething(); } public void OnCallback() { //... } public void Dispose() { m_Proxy.Close(); } } }
Example 5-6. Storing the callback references for later use
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; namespace WCFServiceProgramming.Library { public interface ISomeCallbackContract { [OperationContract] void OnCallback(); } [ServiceContract] public interface IMyContract { [OperationContract] void DoSomething(); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] class MyService : IMyContract { static List<ISomeCallbackContract> m_Callbacks = new List<ISomeCallbackContract>(); public void DoSomething() { ISomeCallbackContract callback = OperationContext.Current.GetCallbackChannel<ISomeCallbackContract>(); if (m_Callbacks.Contains(callback) == false) { m_Callbacks.Add(callback); } } public static void CallClients() { Action<ISomeCallbackContract> invoke = delegate(ISomeCallbackContract callback) { callback.OnCallback(); }; m_Callbacks.ForEach(invoke); } } }
Example 5-7. Configure for reentrancy to allow callbacks
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; namespace WCFServiceProgramming.Library { public interface IMyContractCallback { [OperationContract] void OnCallback(); } [ServiceContract(CallbackContract = typeof(IMyContractCallback))] public interface IMyContract { [OperationContract] void DoSomething(); } [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)] class MyService : IMyContract { public void DoSomething() { IMyContractCallback callback = OperationContext.Current.GetCallbackChannel<IMyContractCallback>(); callback.OnCallback(); } } }
Example 5-8. One-way callbacks are allowed by default
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; namespace WCFServiceProgramming.Library { public interface IMyContractCallback { [OperationContract(IsOneWay = true)] void OnCallback(); } [ServiceContract(CallbackContract = typeof(IMyContractCallback))] public interface IMyContract { [OperationContract] void DoSomething(); } [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)] class MyService : IMyContract { public void DoSomething() { IMyContractCallback callback = OperationContext.Current.GetCallbackChannel<IMyContractCallback>(); callback.OnCallback(); } } }
Example 5-9. Explicit callback connection management
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Transactions; using System.Diagnostics; namespace WCFServiceProgramming.Library { [ServiceContract(CallbackContract = typeof(IMyContractCallback))] interface IMyContract { [OperationContract] void MyMethod(); [OperationContract] void Connect(); [OperationContract] void Disconnect(); } interface IMyContractCallback { [OperationContract] void OnCallback(); } class MyService : IMyContract { static List<IMyContractCallback> _callbacks = new List<IMyContractCallback>(); public void Connect() { IMyContractCallback callback = OperationContext.Current.GetCallbackChannel<IMyContractCallback>(); if (_callbacks.Contains(callback) == false) { _callbacks.Add(callback); } } public void Disconnect() { IMyContractCallback callback = OperationContext.Current.GetCallbackChannel<IMyContractCallback>(); if (_callbacks.Contains(callback) == true) { _callbacks.Remove(callback); } else { throw new InvalidOperationException("Cannot find callback"); } } public static void CallClients() { Action<IMyContractCallback> invoke = delegate(IMyContractCallback callback) { callback.OnCallback(); }; _callbacks.ForEach(invoke); } public void MyMethod() { } } }
Example 5-10. The InstanceContext<T> class
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace WCFServiceProgramming.Library { public class InstanceContext<T> { InstanceContext _instanceContext; public InstanceContext(T implementation) { _instanceContext = new InstanceContext(implementation); } public InstanceContext Context { get { return _instanceContext; } } public T ServiceInstance { get { return (T)_instanceContext.GetServiceInstance(); } } } }
Example 5-11. TheDuplexClientBase<T,C>class
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; using WCFServiceProgramming.Library; namespace WCFServiceProgramming { public abstract class DuplexClientBase<T, C> : DuplexClientBase<T> where T : class { protected DuplexClientBase(InstanceContext<C> context) : base(context.Context) {} protected DuplexClientBase(InstanceContext<C> context, string endpointName) : base(context.Context, endpointName) {} protected DuplexClientBase(InstanceContext<C> context, Binding binding, EndpointAddress remoteAddress) : base(context.Context, binding, remoteAddress) {} protected DuplexClientBase(C callback) : base(callback) {} protected DuplexClientBase(C callback, string endpointName) : base(callback,endpointName) {} protected DuplexClientBase(C callback, Binding binding, EndpointAddress remoteAddress) : base(callback,binding, remoteAddress) { } static DuplexClientBase() { VerifyCallback(); } internal static void VerifyCallback() { Type contractType = typeof(T); Type callbackType = typeof(C); object[] attributes = contractType.GetCustomAttributes( typeof(ServiceContractAttribute), false); if (attributes.Length != 1) { throw new InvalidOperationException("Type of " + contractType + " is not a service contract"); } ServiceContractAttribute serviceContractAttribute; serviceContractAttribute = attributes[0] as ServiceContractAttribute; if (callbackType != serviceContractAttribute.CallbackContract) { throw new InvalidOperationException("Type of " + callbackType + " is not configured as callback contract for " + contractType); } } } public abstract class DuplexClientBase<T> : ClientBase<T> where T : class { protected DuplexClientBase(InstanceContext callbackContext); protected DuplexClientBase(InstanceContext callbackContext, string endpointName); protected DuplexClientBase(InstanceContext callbackContext, Binding binding, EndpointAddress remoteAddress); protected DuplexClientBase(object callbackInstance); protected DuplexClientBase(object callbackInstance, string endpointConfiguratinName); protected DuplexClientBase(object callbackInstance, Binding binding, EndpointAddress remoteAddress); public IDuplexContextChannel InnerDuplexChannel { get; } } public interface IDuplexContextChannel : IContextChannel { InstanceContext CallbackInstance { get; set; } } }
Example 5-12. The DuplexChannelFactory<T,C> class
Example 5-13. Adding duplex support to InProcFactory
Example 5-14. The WsDualProxyHelper class
Example 5-15. The CallbackBaseAddressBehaviorAttribute
Example 5-16. Events management using delegates
Example 5-17. Streaming operations