Wednesday, March 21, 2012

WCF Event Listener


Below code helps to understand establishing a duplex communication channel between service and client.
Below example is case like “WCF Service” will publish the news and the client which is registered in start up to listen to the news updates.
Contract Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Runtime.Serialization;
using System.Diagnostics;

namespace Contracts
{
    public enum Event
    {
        Event1 = 1, Event2 = 2, Event3 = 3, EventAll = 4
    }

    [ServiceContract(CallbackContract = typeof(INewsSubscriber))]
    public interface INewsPublisher
    {
        [OperationContract]
        void Subscribe(Event argEvent);

        [OperationContract]
        void UnSubscribe(Event argEvent);
    }

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    public class NewsPublisher : INewsPublisher
    {
        static Action actEvent1 = delegate { };
        static Action<int> actEvent2 = delegate { };
        static Action<int, string> actEvent3 = delegate { };
        public void Subscribe(Event argEvent)
        {
            INewsSubscriber objNewsSubscriber = OperationContext.Current.GetCallbackChannel<INewsSubscriber>();
            switch (argEvent)
            {
                case Event.EventAll:
                    actEvent1 += objNewsSubscriber.OnEvent1;
                    actEvent2 += objNewsSubscriber.OnEvent2;
                    actEvent3 += objNewsSubscriber.OnEvent3;
                    break;
                case Event.Event1:
                    actEvent1 += objNewsSubscriber.OnEvent1;
                    break;
                case Event.Event2:
                    actEvent2 += objNewsSubscriber.OnEvent2;
                    break;
                case Event.Event3:
                    actEvent3 += objNewsSubscriber.OnEvent3;
                    break;
                default:
                    throw new Exception("Unknown state");
            }
        }

        public void UnSubscribe(Event argEvent)
        {
            INewsSubscriber objNewsSubscriber = OperationContext.Current.GetCallbackChannel<INewsSubscriber>();
            switch (argEvent)
            {
                case Event.EventAll:
                    actEvent1 -= objNewsSubscriber.OnEvent1;
                    actEvent2 -= objNewsSubscriber.OnEvent2;
                    actEvent3 -= objNewsSubscriber.OnEvent3;
                    break;
                case Event.Event1:
                    actEvent1 -= objNewsSubscriber.OnEvent1;
                    break;
                case Event.Event2:
                    actEvent2 -= objNewsSubscriber.OnEvent2;
                    break;
                case Event.Event3:
                    actEvent3 -= objNewsSubscriber.OnEvent3;
                    break;
                default:
                    throw new Exception("Unknown state");
            }
        }

        public void UpdateNews(string news, int state)
        {
            if (state == 0)
            {
                actEvent1();
                return;
            }
            if (state != 0 && news == null)
            {
                actEvent2(state);
                return;
            }
            if (state != 0 && news != null)
            {
                actEvent3(state, news);
                return;
            }
        }
    }

    public interface INewsSubscriber
    {
        [OperationContract(IsOneWay = true)]
        void OnEvent1();
        [OperationContract(IsOneWay = true)]
        void OnEvent2(int state);
        [OperationContract(IsOneWay = true)]
        void OnEvent3(int state, string news);
    }
}


WCF Service Host (News Publisher)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace Host
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                StringBuilder objBuilder = new StringBuilder();
                objBuilder.AppendLine("Press 1 to generate dummy news");
                objBuilder.AppendLine("Press 2 to generate state news");
                objBuilder.AppendLine("Press 3 to generate state news and info");
                objBuilder.AppendLine("Press any other key to exit");
                Contracts.NewsPublisher objPublisher = new Contracts.NewsPublisher();
                ServiceHost objHost = new ServiceHost(typeof(Contracts.NewsPublisher));
                objHost.Open();
                while (true)
                {
                    Console.WriteLine(objBuilder.ToString());
                    switch (Console.ReadLine())
                    {
                        case "1":
                            objPublisher.UpdateNews(null, 0);
                            break;
                        case "2":
                            objPublisher.UpdateNews(null, 1);
                            break;
                        case "3":
                            Console.WriteLine("Please enter news");
                            objPublisher.UpdateNews(Console.ReadLine(), 1);
                            break;
                        default:
                            return;
                    }
                }

            }
            catch (Exception exp)
            {
                Console.WriteLine("Error : " + exp.Message);
            }
            Console.Read();
        }
    }
}


Service Configuration
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="Contracts.NewsPublisher" behaviorConfiguration="SB">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8174/NewsReader"/>
          </baseAddresses>
        </host>
        <endpoint name="dual" binding="wsDualHttpBinding" contract="Contracts.INewsPublisher"></endpoint>
        <endpoint name="mex"  address="mex" binding="mexHttpBinding"  contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="SB">
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <serviceMetadata httpGetEnabled="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <netTcpBinding>
        <binding name="reliabletcp">
          <reliableSession enabled="true" ordered="true"/>
        </binding>
      </netTcpBinding>
    </bindings>
  </system.serviceModel>
</configuration>

WCF Client (News Subscriber)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace Client
{
    class Program
    {
        public class NewsListener : NewsUpdates.INewsPublisherCallback
        {
            public void OnEvent1(NewsUpdates.OnEvent1 request)
            {
                Console.WriteLine("News Update - dummy");
            }
            public void OnEvent2(NewsUpdates.OnEvent2 request)
            {
                Console.WriteLine("News Update (state) - " + request.state);
            }
            public void OnEvent3(NewsUpdates.OnEvent3 request)
            {
                Console.WriteLine("News Update (state and news) - " + request.state + "\n" + request.news);
            }
        }

        static NewsUpdates.NewsPublisherClient objNewsPublisher = null;
        static void Main(string[] args)
        {
            try
            {
                InstanceContext objContext = new InstanceContext(new NewsListener());
                objNewsPublisher = new NewsUpdates.NewsPublisherClient(objContext);
                ICommunicationObject oo = (objNewsPublisher as ICommunicationObject);
                NewsUpdates.SubscribeRequest objRequest = new NewsUpdates.SubscribeRequest();
                objRequest.argEvent = NewsUpdates.Event.EventAll;
                objNewsPublisher.Subscribe(objRequest);
                AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit);
                Console.WriteLine("Listening for news updates....");
                Console.Read();
            }
            catch (Exception exp)
            {
                Console.WriteLine("Error : " + exp.Message);
            }
        }

        static void CurrentDomain_ProcessExit(object sender, EventArgs e)
        {
            NewsUpdates.UnSubscribeRequest objRequest = new NewsUpdates.UnSubscribeRequest();
            objRequest.argEvent = NewsUpdates.Event.EventAll;
            objNewsPublisher.UnSubscribe(objRequest);
        }
    }
}



No comments: