Salesforce Workflow Outbound Messages Handled in .Net with WCF
- Posted in:
- salesforce
- .net
- integration
(Puedes ver este artículo en español aquí)
A high percentage of the Salesforce projects I’ve seen require some kind of integration with backends systems, such as ERPs, databases, or even other CRMs. Salesforce offers a lot of options to make these integrations possible: workflow outbound messages, SOAP/REST callouts, batch integration with DataLoader, etc. Of course there’s lot of documentations on the topic, but most is based on Java and there is little documentation on using .Net. I’m a .Net developer, so I will provide a few examples on how to integrate with Salesforce using Visual Studio and C#. I can’t cover all the possible integration scenarios in just one article, so I will focus on one scenario that I consider as my first option when I design the architecture of a solution that requires integration: workflow outbound messages.
The Requirements
Allow me explain the concepts by using a concrete example: one of our clients required to integrate their Salesforce org with their ERP. Both Salesforce and the ERP manage accounts, but they required Salesforce to be the master system for accounts. The integration was based on the following rules:
- Accounts are created in Salesforce (not in the ERP)
- Any modification to account data in Salesforce needs to be reflected on the ERP (if the account exists in the ERP)
The above rules translate to the following:
- We can create a trigger on account that does a callout to a web service in the ERP, but there’s an easier way (less code): use a workflow outbound message.
- We will use a workflow outbound message in account to fire when the account is created or modified and send some of the fields we need to our ERP.
Let’s dive into code!
Workflow Outbound Message in Salesforce
In Salesforce, go to “Setup->Build->Create->Workflow & Approvals->Workflow Rules”. Create a new rule, and base it on the Account object:
Click next. Give it a name (I will use UpdateAccountOnERP) and for the evaluation criteria specify “created, and every time it’s edited”. We want this option since we would like to update the account on the ERP every time a modification is made in Salesforce. We want this workflow to be triggered every time, so for the rule criteria, choose “formula evaluates to true”, and specify true as the formula. The workflow should look as this:
Click save and next. On the next screen you will add a workflow action. Click on the “Add Workflow Action” dropdown and select “New Outbound Message”. On the New Outbound Message screen, give it a name and unique name (I will use SendAccountToERP), for the endpoint put any url since we don’t have our web service yet, and finally select all the fields that you would like to send to the ERP. When finished, click Done. You will be taken to the workflow rule screen. Make sure you activate the workflow (click on the “Activate” button).
We have created an outbound message that will be triggered when an account is modified, but the outbound message will fail since we have not created a web service to handle it. For this we need to get the WSDL of the outbound message. Click on the outbound message description and you will be taken to Workflow Outbound Message Detail:
Click on the hyperlink “Click for WSDL”. A new window with the WSDL will be opened. Save the WSDL to disc (I named it accountWorkflowOutboundMessage.wsdl), we will need it in Visual Studio.
Web Service in Visual Studio (using WCF)
Now that you have the WSDL for the outbound message, we need to create a web service to handle the message. This article gives you an overview of how you can create an asmx web service, but I rather work with WCF instead (asmx is kind of old and WCF is supposed to replace it, isn’t?). Also, I will use Visual Studio 2013 Community edition for this. Open Visual Studio and create a new empty ASP.Net Web Application:
We will add a WCF service to the project. Right click on the project name in Solution Explorer and choose “Add->New Item”. Select “WCF Service”:
Visual Studio will add the required assemblies to the project and will create three files: IAccountNotificationService.cs, AccountNotificationService.svc and the underlying AccountNotificationService.svc.cs. We will need to replace the interface definition to match the definition of the outbound message. Add the WSDL file to your project. Open a command prompt on the directory where the WSDL resides (if you have the Productivity Power Tools extension you can right click on the project in Solution Explorer and choose “Power Commands->Open Command Prompt”). At the command prompt type the following:
svcutil /noconfig /out:IAccountNotificationService.cs accountWorkflowOutboundMessage.wsdl
The svcutil command creates the interface for the web service definition. Here is the output of the command:
Notice that the command replaced the contents of the file IAccountNotificationService.cs with the web service definition of the WSDL. You project should now look like this:
Now, there are some tricks to make this work. Open IAccountNotificationService.cs and change the following:
- Put the whole class inside the namespace of the project (in my case is the name of the project WorkflowNotificationServices)
- Change the name of the interface from NotificationPort to IAccountNotificationService.
- Change the ConfigurationName parameter of the ServiceContractAttribute attribute from NotificationPort to AccountNotificationService.
- Remove the parameter ReplyAction=”*” from the OperationContractAttribute attribute. This is important in order to make things work.
- At the end of the file, remove the interface NotificationPortChannel and the class NotificationPortClient (we don’t need these since they are used by a client consuming the web service).
The interface should now look as the following (highlighted lines are the one that changed):
namespace WorkflowNotificationServices { [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] [System.ServiceModel.ServiceContractAttribute(Namespace = "http://soap.sforce.com/2005/09/outbound", ConfigurationName = "AccountNotificationService")] public interface IAccountNotificationService { // CODEGEN: Generating message contract since the operation notifications is neither RPC nor document wrapped. [System.ServiceModel.OperationContractAttribute(Action = "")] [System.ServiceModel.XmlSerializerFormatAttribute()] [System.ServiceModel.ServiceKnownTypeAttribute(typeof(sObject))] notificationsResponse1 notifications(notificationsRequest request); } /// rest of the code below }
Next, open the file AccountNotificationService.svc.cs, remove the DoWork method and implement the IAccountNotificationService interface (place the cursor on text for the name of the interface and press Crtl+. and choose “Implement interface IAccountNotificationService”).
Finally, open web.config and replace it with the following:
<configuration> <connectionStrings> <add name="ERP" connectionString="Data Source=localhost;Initial Catalog=ERP;Integrated Security=True" providerName="System.Data.SqlClient"/> </connectionStrings> <system.web> <compilation debug="true" targetFramework="4.5" /> <httpRuntime targetFramework="4.5" /> <webServices> <protocols> <clear/> <add name="HttpSoap" /> <add name="Documentation"/> </protocols> </webServices> </system.web> <system.serviceModel> <services> <service name="WorkflowNotificationServices.AccountNotificationService"> <endpoint binding="basicHttpBinding" contract="AccountNotificationService"/> </service> </services> <behaviors> <serviceBehaviors> <behavior name=""> <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" /> <serviceDebug includeExceptionDetailInFaults="false" /> </behavior> </serviceBehaviors> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" /> </system.serviceModel> </configuration>
Notice in line 3 I’ve created a connection string to the database for the ERP. I will use this later in the implementation of the web service. Also notice the declaration of the web service in lines 18-20.
You project should compile at this point. Also, we should be able to get the WSDL for our web service. Run the project and using a browser connect to the web service. You should see something like this:
All we have left is to write the code to update our ERP. In this example I will just call a stored procedure on a SQLServer database to update the ERP (is just an example, you can do here whatever you need to communicate with the backend). My service implementation looks like this:
using System; using System.Collections.Generic; using System.Configuration; using System.Data; using System.Data.SqlClient; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.Text; namespace WorkflowNotificationServices { public class AccountNotificationService : IAccountNotificationService { public notificationsResponse1 notifications(notificationsRequest request) { notifications notifications1 = request.notifications; AccountNotification[] accountNotifications = notifications1.Notification; foreach (AccountNotification accountNotification in accountNotifications) { Account account = (Account)accountNotification.sObject; ConnectionStringSettings connectionString = ConfigurationManager.ConnectionStrings["ERP"]; using (SqlConnection cn = new SqlConnection(connectionString.ConnectionString)) { using (SqlCommand command = new SqlCommand("salesforce_updateAccount", cn)) { command.CommandType = CommandType.StoredProcedure; command.Parameters.Add("@idSalesforce", SqlDbType.VarChar).Value = account.Id; command.Parameters.Add("@name", SqlDbType.VarChar).Value = account.Name; command.Parameters.Add("@number", SqlDbType.VarChar).Value = (object)account.AccountNumber ?? DBNull.Value; command.Parameters.Add("@address", SqlDbType.VarChar).Value = (object)account.BillingStreet ?? DBNull.Value; command.Parameters.Add("@city", SqlDbType.VarChar).Value = (object)account.BillingCity ?? DBNull.Value; command.Parameters.Add("@state", SqlDbType.VarChar).Value = (object)account.BillingState ?? DBNull.Value; command.Parameters.Add("@postalCode", SqlDbType.VarChar).Value = (object)account.BillingPostalCode ?? DBNull.Value; command.Parameters.Add("@country", SqlDbType.VarChar).Value = (object)account.BillingCountry ?? DBNull.Value; cn.Open(); command.ExecuteNonQuery(); } } } notificationsResponse response = new notificationsResponse(); response.Ack = true; return new notificationsResponse1() { notificationsResponse = response }; } } }
It is important to set the Ack variable of the response to true, otherwise Salesforce will think there was an error and will keep the outbound message in the queue and retry to send it at regular intervals.
Testing the Outbound Message
We need to publish our web service and make it available on the internet. The publishing is outside of the scope of this article. In my case I published it on an IIS server in our DMZ, and the public url is http://http.grupolanka.com/Salesforce/WorkflowNotificationServices/AccountNotificationService.svc (don’t try it, it won’t work)
Now I need to go back to Salesforce and change the url of the outbound message I created earlier. In Salesforce go to “Setup->Build->Create->Workflow & Approvals->Outbound Messages”. Edit the outbound message definition (in the example is SendAccountToERP) and edit the “Endpoint URL” field:
Save it. Open an account and make a modification, Salesforce will now call the web service passing the fields specified in the outbound message definition.
And that’s it! You can now have Salesforce to “talk” to your backend. In a next article I will complicate things a bit and let the web service call back to Salesforce for additional data. You can get the sample project here: