Click here to Skip to main content
15,886,835 members
Articles / Programming Languages / C# 4.0

Understanding Events in WCF

Rate me:
Please Sign up or sign in to vote.
3.61/5 (8 votes)
4 Oct 2013CPOL6 min read 55.9K   3.3K   31   6
This article explains the working of events in WCF.

Introduction

Events allow the client or clients to be notified that something has occurred on the service side. An event may result from a direct client call, or may be the result of something the service monitors.

Background

While events in WCF are nothing more than call back operations, however by their very nature, events usually imply a looser relationship between the publisher and the subscriber than a typical relationship between a client and a service. The Service firing the event is called the publisher, and the client receiving the event is called the subscriber. service typically publishes the same event to multiple subscribing clients. The publisher often does not care about the order of invocation of the subscribers, or any errors, all the publisher knows is that it should deliver the event to the subscribers. Since service does not care about the returned results from the subscribers.

Consequently, event handling operations:

  • should have void return type
  • should not have any out/ref parameters
  • should be marked as one way

Using the Code

This article has been divided into 3 modules:

  • WCF Service Library (EventsLib.dll): Actual Service logic, which defines a Service Contract Interface, OperationContract, and implements them, and exposes few functions to the world to use them
  • Console based Host Application to host the WCF Service Library EventsLibHost.exe): Host the WCF library
  • Console Client Application (EventsClient.exe): Client Application which will use this service.

First Module: WCF Service Library (EventsLib.dll)

To create this project, you can simply take a "Class Library" project, while choosing from project wizard option. Let's name it "EventsLib", it is the actual service which implements the business logic. The project already contains a file Class1.cs, let us do some house keeping, before we write any code for the service library.

Little Housekeeping

  • Delete the file Class1.cs from the project workspace.
  • Add a new Interface named ICalcService to the project, a new file ICalcService.cs will be added to the project.
  • Add a new Class named CalcService to the project that will implement the ICalcService interface, a new file CalcService.cs will be added to the project.
  • Add a new Interface named ICalcServiceEvents to the project, a new file ICalcServiceEvents.cs will be added to the project.

Defining Interface ICalcServiceEvents (ICalcServiceEvents.cs)

So let's define interface for the events published by the service.

C#
using System;
using System.Text;
using System.ServiceModel;
namespace EventsLib
{
    public interface ICalcServiceEvents
    {
        [OperationContract(IsOneWay=true)]
        void Calculated(int nOp, double dblNum1, double dblNum2, double dblResult);
 
        [OperationContract(IsOneWay=true)]
        void CalculationFinished();
    }
}
Explanation

Interface simply publishes 2 methods which are basically events for the subscriber. Note that every method in this interface has been:

  • marked as OneWay
  • does not return any value (void)
  • has no out/ref parameter

The first method Calculated is an event for the subscriber, it is fired, when calculation is done, it also passes the result to the subscribers. along with the operands and operation type.

The second method CalculationFinished is the event, it is fired when the Calculation is finished.

Defining Interface ICalcService (ICalcService.cs)

So let's define interface for the service.

C#
//  Listing of ICalcService.cs
using System;
using System.Text;
using System.ServiceModel;
namespace EventsLib
{
    [ServiceContract(CallbackContract = typeof(ICalcServiceEvents))]
    public interface ICalcService
    {
        [OperationContract]
        void Calculate(int nOp, double dblNum1, double dblNum2);
 
        [OperationContract]
        void SubscribeCalculatedEvent();
 
        [OperationContract]
        void SubscribeCalculationFinishedEvent();
    }
}
Explanation

Interface simple defines 3 methods.

The first method...

C#
void Calculate(int nOp, double dblNum1, double dblNum2);

...is the method, that is related to business logic that does the actual job.

The other two methods...

C#
void SubscribeCalculatedEvent () ;
void SubscribeCalculationFinishedEvent (); 

...are helper methods that are called by the client to subscribe an event published by the service. As you have 2 events, so you have one method for each event.

Implementing ICalcService interface (CalcService.cs)

Let 's implement each of the methods defined:

C#
 //  Listing of CalcService.cs

using System;
using System.Text;
using System.ServiceModel;
 
namespace EventsLib
{
    public  class CalcService : ICalcService
    {
       static Action<int, double, double, double> m_Event1 = delegate { }; 
       static Action m_Event2 = delegate { };
 
       public void SubscribeCalculatedEvent()
       {
           ICalcServiceEvents subscriber = 
           OperationContext.Current.GetCallbackChannel<ICalcServiceEvents>();
           m_Event1 += subscriber.Calculated;
       }
        
        public void SubscribeCalculationFinishedEvent()
        {
            ICalcServiceEvents subscriber = 
            OperationContext.Current.GetCallbackChannel<ICalcServiceEvents>();
            m_Event2 += subscriber.CalculationFinished ;
        }
               
        public void Calculate(int nOp, double dblX, double dblY)
        {
            double dblResult = 0;
            switch (nOp)
            {
                case 0: dblResult = dblX + dblY; break;
                case 1: dblResult = dblX - dblY; break;
                case 2: dblResult = dblX * dblY; break;
                case 3: dblResult = (dblY == 0) ? 0 : dblX / dblY; break;
            }
            
            m_Event1(nOp, dblX, dblY, dblResult);
            m_Event2();
        }
    }
}    
Explanation
Member variables
C#
static Action<int, double, double, double> m_Event1 = delegate { };
static Action m_Event2 = delegate { };

Action keyword can be used to define a delegate, can be used to pass a method as a parameter without explicitly declaring a custom delegate.

Methods SubscribeCalculatedEvent() and SubscribeCalculationFinishedEvent() are as given below:

C#
public void SubscribeCalculatedEvent()
{
    ICalcServiceEvents subscriber = 
		OperationContext.Current.GetCallbackChannel<ICalcServiceEvents>();
    m_Event1 += subscriber.Calculated;
}   
C#
public void SubscribeCalculationFinishedEvent()
{
    ICalcServiceEvents subscriber=OperationContext.Current.GetCallbackChannel<ICalcServiceEvents>();
    m_Event2 += subscriber.CalculationFinished ; 
}

Gets the reference of the client side callback method, and assigns this event handler to the delegate variable m_Event1, and m_Event2. This method allows clients to do selective subscription of the exposed events.

Method public void Calculate(int nOp, double dblX, double dblY) is as follows:

C#
public void Calculate(int nOp, double dblX, double dblY)
{
            double dblResult = 0;
            switch (nOp)
            {
                case 0: dblResult = dblX + dblY; break;
                case 1: dblResult = dblX - dblY; break;
                case 2: dblResult = dblX * dblY; break;
                case 3: dblResult = (dblY == 0) ? 0 : dblX / dblY; break;
            }
            
            m_Event1(nOp, dblX, dblY, dblResult);
            m_Event2();
}   

implements the business logic of the service, and calls the callback methods implemented at the client side, using delegates, in short, fires events.

Compile the class library, and the first part is complete.

Second Module: Host Application (EventsLibHost.exe)

To create this project, you can simply take a "Console based Application" project, while choosing from project wizard option. let's name it "EventsLibHost", a new Project EventsLibHost will be added to the workspace.

  • Add reference of EventsLib Project to the Host application, as we are going to host the library.

Assumption

The Host application will expose the following endpoints for the service:

  • CalcService will expose HTTP endpoint at Port 9011
  • Corresponding mex End Point (IMetadatExchange) for the HTTP end point

Defining Configuration for CalcService

XML
<!--********************************** Calc Service ********************************** -->
     <service name="EventsLib.CalcService"
     behaviorConfiguration="CalcServiceBehavior">
       <host>
         <baseAddresses>
           <add baseAddress="http://localhost:9011/CalcService"/>
         </baseAddresses>
       </host>

       <endpoint address="" binding="wsDualHttpBinding"
       contract="EventsLib.ICalcService"/>
       <endpoint address="mex" binding="mexHttpBinding"
       contract="IMetadataExchange"/>
     </service>
   </services>
Explanation

Application configuration files define 1 endpoint with WSDualHttpBinding at port 9011 and respective mex end point. WSDualHttpBinding is similar to the WSHttpBinding, but provides more web service features.

Defining Behavior(s) for Services

XML
<!-- ********************************** behaviors ********************************** -->
   <behaviors>
     <serviceBehaviors>
       <!-- CalcService Behavior -->
       <behavior name="CalcServiceBehavior">
         <serviceMetadata httpGetEnabled="true"/>
         <serviceDebug includeExceptionDetailInFaults="true "/>
       </behavior>
     </serviceBehaviors>
   </behaviors>
Explanation

Each service behavior defines two attributes:

XML
<serviceMetadata httpGetEnabled="true"/>
Explanation

Gets or sets a value that indicates whether to publish service metadata for retrieval using an HTTP/GET request, true means yes, metadata is available for retrieval using a HTTP/GET request.

XML
<serviceDebug includeExceptionDetailInFaults="true "/> 
Explanation

Set IncludeExceptionDetailsInFaults to true to enable clients to obtain information about internal service method exceptions; it is only recommended as a way of temporarily debugging a service application. This property must be set to false on production servers. Once the configuration is in place, let's write the code to host the services.

C#
try
{
    ServiceHost host = new ServiceHost(typeof(EventsLib.CalcService));
    host.Open();
    Console.WriteLine("Service is Hosted as http://localhost:9011/CalcService");
    Console.WriteLine("\nPress  key to stop the service.");
    Console.ReadLine();
    host.Close();
}
catch (Exception eX)
{
    Console.WriteLine("There was en error while Hosting Service [" + eX.Message +"]" );
    Console.WriteLine("\nPress  key to close.");
    Console.ReadLine();
}
Explanation

The ServiceHost class provides a host for services. You simply create the host object and open it, and wait endlessly, till a key is pressed. when you press a key, host is closed.

Build the project.

Open a new command prompt with administrative privilege (Run as Administrator), and execute the host application, as shown below:

Image 1

Third Module: Client Application (EventsClient.exe)

To create this project, you can simply take a "Console based Application" project, while choosing from project wizard option. Let's name it "EventsClient ", a new Project EventsClient will be added to the workspace.

Generating Proxy

While the host application is running, right click on the client application project, click on

Generate Proxy for CalcService

References –> Add Service Reference

In the address bar, type the address of the mex endpoint address of CalcService as shown below:

Image 2

Now when you have added reference of the CalcService, now the next thing you need to do is to define an event handler for the events fired for the service.

Adding Event Handler

Add a new class to the client application, let's call it CalcServiceCallbackSink, it is derived from the CallBack interface of the service (ICalcServiceCallback), and simply implements the methods exposed by the Callback interface as shown below:

C#
class CalcServiceCallbackSink:CalcServiceReference.ICalcServiceCallback 
{
    public void Calculated(int nOp, double dblNum1, double dblNum2, double dblResult)
    {
        switch (nOp)
        {
            case 0: Console.WriteLine("\nOperation : Addition"); break;
            case 1: Console.WriteLine("\nOperation : Subtraction"); break;
            case 2: Console.WriteLine("\nOperation : Multiplication"); break;
            case 3: Console.WriteLine("\nOperation : Division"); break;
        }
        Console.WriteLine("Operand 1 ...: {0}", dblNum1);
        Console.WriteLine("Operand 2 ...: {0}", dblNum2);
        Console.WriteLine("Result ......: {0}", dblResult); 
    }

    public void CalculationFinished()
    {
        Console.WriteLine("Calculation completed");
    }
}

Once the event handlers for the service have been implemented, it is now time to call the service.

Calling CalcService
C#
CalcServiceCallbackSink objsink = new CalcServiceCallbackSink ();
InstanceContext iCntxt = new InstanceContext(objsink);
                 
CalcServiceReference.CalcServiceClient objClient = 
		new CalcServiceReference.CalcServiceClient(iCntxt);
objClient.SubscribeCalculatedEvent ();
objClient.SubscribeCalculationFinishedEvent ();
            
double dblNum1 = 1000, dblNum2 = 2000 ;
objClient.Calculate (0, dblNum1, dblNum2);

dblNum1 = 2000; dblNum2 = 4000;
objClient.Calculate(1, dblNum1, dblNum2);

dblNum1 = 2000; dblNum2 = 4000;
objClient.Calculate(2, dblNum1, dblNum2);

dblNum1 = 2000; dblNum2 = 400;
objClient.Calculate(3, dblNum1, dblNum2);
Explanation

Create an object of Events Sink object, create an Instance context object passing the object of the class who implements the callback interface as an Instance Context information for the service.

C#
CalcServiceCallbackSink objsink = new CalcServiceCallbackSink ();
InstanceContext iCntxt = new InstanceContext(objsink); 

Create an object of the service proxy.

C#
CalcServiceReference.CalcServiceClient objClient = 
new CalcServiceReference.CalcServiceClient(iCntxt);

Select the events you prefer to receive.

C#
objClient.SubscribeCalculatedEvent ();
objClient.SubscribeCalculationFinishedEvent (); 

Call the service methods.

C#
double dblNum1 = 1000, dblNum2 = 2000 ;
objClient.Calculate (0, dblNum1, dblNum2);

dblNum1 = 2000; dblNum2 = 4000;
objClient.Calculate(1, dblNum1, dblNum2);

dblNum1 = 2000; dblNum2 = 4000;
objClient.Calculate(2, dblNum1, dblNum2);

dblNum1 = 2000; dblNum2 = 400;
objClient.Calculate(3, dblNum1, dblNum2); 

Build and execute, and here is the output:

Image 3

That's all folks.

Points of Interest

This article explains the events aspect, in a WCF service, how to fire events with and without arguments, and how to handle them.

History

  • Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
India India
More than 10 years of experience in designing and development of GUIs and Middleware for industrial control systems.

Comments and Discussions

 
QuestionShould also unsubscribe from events Pin
Boaz Feldbaum21-Mar-20 0:03
Boaz Feldbaum21-Mar-20 0:03 
QuestionSource Files are diffrent Pin
mahdi87_gh25-Dec-15 21:53
mahdi87_gh25-Dec-15 21:53 
AnswerRe: Source Files are diffrent Pin
Praveen Kumar Katiyar1-Jan-16 3:33
professionalPraveen Kumar Katiyar1-Jan-16 3:33 
QuestionWhere is the ICalcServiceCallback interface defined? Pin
Brady Kelly28-Feb-15 20:35
Brady Kelly28-Feb-15 20:35 
AnswerRe: Where is the ICalcServiceCallback interface defined? Pin
Praveen Kumar Katiyar1-Jan-16 3:40
professionalPraveen Kumar Katiyar1-Jan-16 3:40 
QuestionNice approach Pin
pkfox2-Apr-14 20:02
professionalpkfox2-Apr-14 20:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.