Creating an Outbound Messaging Notification Service with CSharp and .Net Framework 2.0

Before going through this sample, please familiarize yourself with Outbound Messaging by reading the entire Outbound Messaging topic in the Web Services API Developer Guide.

The .Net Framework 2.0 provides excellent tools for working with web services. In this sample we will demonstrate how to create an Outbound Messaging (OM) service from scratch in C# using the .Net Framework 2.0.


Download the sample code The sample code zip file contains two projects. One, called the OM_Sample, is web project that provides the messaging services. The other, called TestClient, provides a simple testing client to test the local messaging services. You may need to add the OM_Sample project manually to your solution.


Contents

Getting Started

Create a new Blank Solution and Emplty Web Site

To begin create a new Blank Solution in Visual Studio named OMSolution.

Image:NewBlankSolution.JPG

Next, add a new Empty Web Site to the solution named OMSite. Create the site within the folder created for the solution as shown in the Location string below. Select Visual C# as the Language.

Image:NewEmptyWebsite.JPG


Generate WSDL File

To obtain your WSDL you will need to login to your salesforce.com account. Click on the Setup link in the upper right portion of the page to get to the Setup area of the application. From the navigation list on the left, expand the Customize item under App Setup. From here you need to expand the Workflow & Approvals item and select Outbound Messages. You should now see a list of Outbound Messages that have been created in your account.


Image:Messages.jpg


Find the message that you want to test and click it to bring up the detail screen for that message.


Image:Messagedetail.jpg


Notice the field in the detail screen labeled "Endpoint WSDL". Clicking this link will cause your Outbound Message WSDL to be generated and shown in the browser. To save the actual WSDL file, right click the "Click for WSDL" link and select "Save Link As..." if you are using Firefox or, if you are using Internet Explorer select "Save target as...". Navigate to where you web project is located and save the file there, for instance "Visual Studio 2005/Projects/OMSolution/OMSite". Be sure to give the file an extension of wsdl.

Generate Web Service

Now that we have the project and the WSDL file, we can generate the actual code that will implement the service. The first step is to create a new web service using the Visual Studio IDE.

The WSDL file used in this sample is for an Outbound Message generated by workflow created on an Account, therefore, most of the naming of services and files starts with Account. You can use your own naming generation for your Outbound Messaging service.

Right-click the project OMSite and select "Add New Item...". From the list of templates find and select "Web Service" and name the service "AccountNotificationService.asmx". Make sure that "Visual C#" is selected as the language and that "Place code in separate file" is checked. Click the "Add" button to create the service.


Image:NewWebService.JPG


Using the "Visual Studio 2005 Command Prompt", open a new command window. Change you current directory to the directory that contains the WSDL file that you downloaded previously, for example "Visual Studio 2005/Projects/OMSolution/OMSite".

The web service is generated by use the wsdl.exe program that is part of the .NET Framework. At the command line prompt enter "wsdl /language:CS /out:App_Code/AccountNotificationService.cs /serverInterface /namespace:AccountNotification om.wsdl". This runs the wsdl tool and specifies that that language to generate the code is is C# (CS) and to name the generated file AccountNotificationService.cs and to place the generated file in the App_Code folder within the OMSite project. The interface that is created will have AccountNotification as the namespace. Using the namespace option is important if you are planning on implementing more than one Outbound Messaging Service on your server. Using the /serverInterface switch will cause an interface to be generated based on the wsdl file specified as the last argument in the command line.

Notice that we used the same name for the generated file as for the Web Service item that we created above.


Image:WsdlGeneration.JPG

At this point the project contains a web service definition and an interface that represents the specification of the web service based on the wsdl.


Image:WebServiceProject.JPG

Implement the Web Service

Since at this point there is an interface for the web service the implementation can now be created. By using an interface and an implementation of the interface, you can regenerate the interface later if required without losing the actual code that implements the interface.

Right-click the App_Code folder and select "Add New Item...". Select "Class" from the available templates. Name the class "AccountNotificationServiceImpl.cs" and ensure that "Visual C#" is selected as the language and click the "Add" button.

You should have a class that looks similar to the one shown below.


Image:EmptyImpl.JPG

Remove all the "using" directives for System.Web.UI.*. These are not needed for the web service.

To implement the web service interface, add ": AccountNotification.INotificationBinding" to the class declaration.


public class AccountNotificationServiceImpl : AccountNotification.INotificationBinding

Place your cursor at the end of the line shown above and press the F10 while holding down the SHIFT and ALT key (SHIFT-ALF-F10). This will popup a list of options for implementing the interface. Select "Implement interface 'AccountNotification.INotificationBinding'. This will create a stub for your web service. Your file should look like the one shown below.


using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;

/// <summary>
/// Summary description for AccountNotificationServiceImpl
/// </summary>
public class AccountNotificationServiceImpl : AccountNotification.INotificationBinding
{
    public AccountNotificationServiceImpl()
    {
        //
        // TODO: Add constructor logic here
        //
    }


    #region INotificationBinding Members

    public AccountNotification.notificationsResponse notifications(AccountNotification.notifications notifications1)
    {
        throw new Exception("The method or operation is not implemented.");
    }

    #endregion
}


The final step to implementing the web service is to write the specific code for what you will do with the data that you receive via the web service.

Sample Implementation

This sample simply outputs to the Command window the data sent via the web service. The first step is to cast the notifications from the Force.com Platform into Account Notifications.

AccountNotification.AccountNotification[] accounts = notifications1.Notification;

All notifications will come in a batch as an array. You should not assume that you will ever get anything other than an array sent to your web service.

The sample then begins iterating over the array of Account Notifications. An account notification includes several fields. To learn more about the details of the notification fields see the documentation for the Force.com API.

        for (int i = 0; i < accounts.Length; i++)
        {
            System.Diagnostics.Debug.WriteLine("Notification " + (i + 1));
            AccountNotification.AccountNotification notification = accounts[i];

The field we will concentrate on is the SObject field. This is the object that was configured for Outbound Messaging Workflow and in this case is an Account object. Again, we cast to the specific object.

Account account = (Account)notification.sObject;

We can now examine the fields on the object.

 Debug.WriteLine("\tAccount number: " + account.AccountNumber);
 Debug.WriteLine("\tAccount ID: " + account.Id);
 Debug.WriteLine("\tAccount name: " + account.Name);
 Debug.WriteLine("\tLast activity: " + account.LastActivityDate.ToString());

After we have processed all the objects we will create the response to send back to the service that called the web service.

        notificationsResponse response = new notificationsResponse();
        response.Ack = true;
        return response;


The final sample implementation is shown below:

    notificationsResponse INotificationBinding.notifications(notifications notifications1)
    {
        AccountNotification.AccountNotification[] accounts = notifications1.Notification;
        System.Diagnostics.Debug.WriteLine("\n\n\n\n\n\n\n\n\n\n");
        for (int i = 0; i < accounts.Length; i++)
        {
            System.Diagnostics.Debug.WriteLine("Notification " + (i + 1));
            AccountNotification.AccountNotification notification = accounts[i];
            //Pull the account data out
            AccountNotification.Account account = (AccountNotification.Account)notification.sObject;
            //We will just echo some values to the console
            Debug.WriteLine("\tAccount number: " + account.AccountNumber);
            Debug.WriteLine("\tAccount ID: " + account.Id);
            Debug.WriteLine("\tAccount name: " + account.Name);
            Debug.WriteLine("\tLast activity: " + account.LastActivityDate.ToString());
        }
        //Now, send a response.
        AccountNotification.notificationsResponse response = new AccountNotification.notificationsResponse();
        response.Ack = true;
        return response;
    }
</code>


About the Response

Although the sample shown above process all the records prior to creating and returning it's response, you should endeavor to send the response as quickly as possible. It's good to keep in mind that you might experience network effects that would cause the connection to be dropped or possibly timeout. For a robust Outbound Messaging webservice you should use a durable store to pass your notifications to and keep track of the notification ids that you have processed. You should also provide a mechanism in your implementation to prevent processing a notification more than once.

The rule of thumb is to send your ack once you can guarantee that you can do the work (either because you have already done it or you've copied the notification to a durable store).


Bind the Implementation to the Web Service

The final step in implementing the Notification Service is to modify the binding for the AccountNotificationService.asmx file to the implementation. When we created the web service using Visual Studio, it created an AccountNotificationService.cs file. When then over wrote that file when we used wsdl.exe to generate the service interface and finally created the implementation as AccountNotificationServiceImpl.cs.

The asmx file is now associated with our interface and we need to change the association to the implementation. Open the AccountNotificationService.asmx file in Visual Studio. This file has one line in it and shows the CodeBehind attribute equal to "~/App_Code/AccountNotificationService.cs". Change this to "~/App_Code/AccountNotificationServiceImpl.cs".

The Class attribute is shown as equal to ?AccountNotificationService". Change this to "AccountNotificationServiceImpl". The final asmx file contents is shown below.

<code>
<%@ WebService Language="C#" CodeBehind="~/App_Code/AccountNotificationServiceImpl.cs" Class="AccountNotificationServiceImpl" %>

Your implementation is now complete.

Testing Your Service

Since most of you will be developing your service, at least initially, on a localhost server, the question of how to test the implementation is common. Included in the sample code is a simple web services client that can be used to generate valid requests to your localhost web service.

Once built, you can simply run the testing client as you would any other Windows application. Or, you can include the test client in your solution and run it from the Visual Studio environment.

Setting up for testing in Visual Studio

Add the TestClient project to your solution using "File/Add/Existing Project...". Build the test client to ensure that all dependencies have been satisfied.

To get the most out of the sample you should configure the solution to use both projects at startup.


Image:Setstartup.JPG



Image:SetStartupDialog.jpg

Notice that the web service project is above the TestClient project indicating that the web service project should be started first. Make sure that the Action for each project is set to "Start". Click the Project Dependencies item on the left under "Common Properties". Select TestClient from the drop down list and place a check mark next to "C:\...\OMSite" or what ever the name of your web project is. Click OK to save your settings.

Once these settings have been made, you can set breakpoints in your web service and view the output of your Debug statements in Visual Studio.

The test client simply generates appropriate requests and sends them to your service. You will likely need to change the url of your service. Using the built-in IIS of Visual Studio, you should be able to simply change the port number on the url string and the actual path to your service. The test client uses the paths from the sample provided at the top of this article.


Image:OMSampleTester.JPG

To begin using the tester, click the Load Accounts button, or if the Contact Service radio button is selected, click the Load Contacts button. In either case, only ten records will be loaded, so you don't have to worry about pulling a lot of data. You will be asked to login to your account and the listbox will be filled with some of your live data. Select a record from the list box. The details of the record are shown below the listbox.

Verify that the url for your service is correctly entered into the Endpoint Url text box. To determine the port for your Visual Studio debug session, hover your mouse over the "ASP .NET Developer Server" icon in your system tray.

Image:AspServer.JPG

You can now click the Test Service button. Verify that you are getting an ack or nack from your web service. To more closely examine the data contained in the request, set a breakpoint in your implementation file and examine the objects.

You should be able to modify the TestClient to generate the appropriate messages that your Outbound Messaging solution requires.