17
votes

I am looking at implementing listener application for Salesforce Outbound Messaging.

The walk through implements it using the deprecated ASMX web service. The code is generated using wsdl.exe with /serverInterface switch.

Here is the wsdl of Salesforce Outbound Messaging.

<?xml version="1.0" encoding="UTF-8"?>

<definitions targetNamespace="http://soap.sforce.com/2005/09/outbound"
   xmlns="http://schemas.xmlsoap.org/wsdl/"
   xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
   xmlns:tns="http://soap.sforce.com/2005/09/outbound"
   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
   xmlns:ent="urn:enterprise.soap.sforce.com"
   xmlns:ens="urn:sobject.enterprise.soap.sforce.com">
<types>

    <schema elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:enterprise.soap.sforce.com">
        <!-- Our simple ID Type -->
        <simpleType name="ID">
            <restriction base="xsd:string">
                <length value="18"/>
                <pattern value='[a-zA-Z0-9]{18}'/>
            </restriction>
        </simpleType>
    </schema>

    <schema elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="urn:sobject.enterprise.soap.sforce.com">
        <import namespace="urn:enterprise.soap.sforce.com" />
        <!-- Base sObject (abstract) -->
        <complexType name="sObject">
            <sequence>
                <element name="fieldsToNull" type="xsd:string" nillable="true" minOccurs="0" maxOccurs="unbounded"/>
                <element name="Id" type="ent:ID" nillable="true" />
            </sequence>
        </complexType>

        <complexType name="AggregateResult">
            <complexContent>
                <extension base="ens:sObject">
                    <sequence>
                        <any namespace="##targetNamespace" minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
                    </sequence>
                </extension>
            </complexContent>
        </complexType>

        <complexType name="Contact">
            <complexContent>
                <extension base="ens:sObject">
                    <sequence>
                    <element name="Email" nillable="true" minOccurs="0" type="xsd:string"/>
                    <element name="FirstName" nillable="true" minOccurs="0" type="xsd:string"/>
                    <element name="LastName" nillable="true" minOccurs="0" type="xsd:string"/>
                    </sequence>
                </extension>
            </complexContent>
        </complexType>
    </schema>

    <schema elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://soap.sforce.com/2005/09/outbound">
        <import namespace="urn:enterprise.soap.sforce.com" />
        <import namespace="urn:sobject.enterprise.soap.sforce.com" />

        <element name="notifications">
            <complexType> 
                <sequence> 
                    <element name="OrganizationId" type="ent:ID" />
                    <element name="ActionId" type="ent:ID" />
                    <element name="SessionId" type="xsd:string" nillable="true" />
                    <element name="EnterpriseUrl" type="xsd:string" />
                    <element name="PartnerUrl" type="xsd:string" />
                    <element name="Notification" maxOccurs="100" type="tns:ContactNotification" />
                </sequence> 
            </complexType> 
        </element>

        <complexType name="ContactNotification">
            <sequence>
                <element name="Id" type="ent:ID" />
                <element name="sObject" type="ens:Contact" />
            </sequence>
        </complexType>

        <element name="notificationsResponse">
            <complexType>
                <sequence>
                    <element name="Ack" type="xsd:boolean" />
                </sequence>
            </complexType>
        </element>
    </schema>
</types>


<!-- Method Messages -->   
<message name="notificationsRequest">
    <part element="tns:notifications" name="request"/>
</message>
<message name="notificationsResponse">
    <part element="tns:notificationsResponse" name="response"/>
</message>

<!-- PortType -->
<portType name="NotificationPort">
    <operation name="notifications">
        <documentation>Process a number of notifications.</documentation>
        <input  message="tns:notificationsRequest"/>
        <output message="tns:notificationsResponse"/>
    </operation>
</portType>

<!-- Binding 
     You need to write a service that implements this binding to receive the notifications
 -->
<binding name="NotificationBinding" type="tns:NotificationPort">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>

    <operation name="notifications">
        <soap:operation soapAction=""/>
        <input>
            <soap:body use="literal"/>
        </input>
        <output> 
            <soap:body use="literal"/>
        </output>
    </operation>
</binding>

<!-- Service Endpoint -->
<service name="NotificationService">
    <documentation>Notification Service Implementation</documentation>
    <port binding="tns:NotificationBinding" name="Notification">
        <soap:address location="http://www.myserver.com/salesforceoutboundprototype/notificationport.svc"/>
    </port>
</service>    
</definitions>

tldr is I need to implement NotificationBinding so that Salesforce can call my webservice when an event occurs on their system.

I since have realised svcutil does not natively support Contract-First development.

As per Contract-First SOA with WCF I used WSCF.Blue to generate server-side stubs from Salesforce wsdl. Whilst the code compiles wsdl generated by my service does not have the required notifications operation.

I wonder what I am going wrong?


So I managed to do quick implementation of Salesforce wsdl using wsdl.exe and /serverInterface and it seems the wsdl generated by asmx based application is quite different from wcf based application.

This is the interface created by wsdl.exe with /serverInterface

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "4.0.30319.1")]
[System.Web.Services.WebServiceBindingAttribute(Name="NotificationBinding", Namespace="http://soap.sforce.com/2005/09/outbound")]
[System.Xml.Serialization.XmlIncludeAttribute(typeof(sObject))]
public interface INotificationBinding {

    /// <remarks/>
    [System.Web.Services.WebMethodAttribute()]
    [System.Web.Services.Protocols.SoapDocumentMethodAttribute("", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)]
    [return: System.Xml.Serialization.XmlElementAttribute("notificationsResponse", Namespace="http://soap.sforce.com/2005/09/outbound")]
    notificationsResponse notifications([System.Xml.Serialization.XmlElementAttribute("notifications", Namespace="http://soap.sforce.com/2005/09/outbound")] notifications notifications1);
}

This is the interface created by WSCF.Blue

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(Namespace="http://soap.sforce.com/2005/09/outbound", ConfigurationName="INotificationPort")]
public interface INotificationPort
{

    // CODEGEN: Generating message contract since the operation notifications is neither RPC nor document wrapped.
    [System.ServiceModel.OperationContractAttribute(Action="", ReplyAction="*")]
    [System.ServiceModel.XmlSerializerFormatAttribute(SupportFaults=true)]
    [System.ServiceModel.ServiceKnownTypeAttribute(typeof(sObject))]
    notificationsResponse1 notifications(notificationsRequest request);
}

They seem to be fairly similar so I don't know why wsdl generated by these application would be so different? Is it worthwhile adding wsdls (don't want to make the question any longer then it already is)?

5
I believe that in WCF the generated client interface is substantially identical to the server interface.millimoose
Hmm what does that mean for my purposes?mob1lejunkie
Use svcutil to generate shells for your messaging classes. They'll basically be the same whether you're writing a client or serverM.Babcock
Not very helpful to you now but WCF 4.5 will reintroduce this lost functionalityRichard Blewett

5 Answers

5
votes

You may not like what I suggest but I believe it will actually be the best option for you. Use ASMX. I have three OM listeners and they all work just fine under 4.0

I do not know who spreads these misfinfromations about some .NET2.0 techs but a lot of them are most certainly not obsoleted just because dev community got stricken with novelty fetish; ASMX is one of them (so is Linq2SQL, etc). WCF is simply put not yet complete, it is not fully WS compliant and that casues a lot of grief with salesforce integration (most painful being that WCF does not support soap headers where salesforce keeps session info).

For more info why asmx is NOT obsolete, see here: Does .net 4.0 still support asmx

1
votes

Use svcutil to generate a client proxy and simply delete the client proxy implementation, and keep the service/data contracts. The service contract is symmetrical from a client/server point of view. I have worked on substantial contract-first projects and found this to be perfectly adequate.

Just make sure you have all of the external WSDLs available to for svcutil to resolve. I see several import statements in your WSDL text above.

BTW, WSCF.Blue is not quite up to it unfortunately as it's not been updated for a few months and is unlikely to be so in the future. Sadly, the primary developer died in a car crash last year (yes, really.)

0
votes

I went through the same pain of trying to get my OBM listener working with WCF. I had originally only set up an OperationContract for the notifications method. After deploying I soon realised a Data Contract would need to be set up which I did but due to time constraints and not being entirely sure if that would be all I needed to do I simply switched to an asmx web service which worked.

0
votes

I figured out an easy way to generate WCF interfaces from the Salesforce WSDL using the built in Visual Studio tools. I downloaded the WSDL from Salesforce and saved it to my desktop. In Visual Studio, go to the add service reference menu in one of your projects(doesn't matter where, you're going to delete this). For the Url, type in the path to the WSDL you downloaded locally (i.e. C:\Users\yourusername\Desktop\notification.wsdl)

This should bring load in the data for the service. Click okay to create the reference and now in the services references folder, double click on the reference you just created. This should show you the object browser with the namespace of the service reference you just created being highlighted. Double click on any of the classes inside this namespace and it will open up the related reference.cs that was generated by visual studio.

Copy all of the content inside the namespace declaration to the file of your choice with whatever namespace you decide. You can rename that interface as well if you plan to handle more than one outbound message (default should be NotificationPort). The interface will have references to the namespace you built it with but you can remove all of the namespacing in the file because all of the classes it is referring to are in the same file.

Create a new WCF service class. Delete the interface that is automatically created with the svc file and change the interface name to the one that you just copied to the new file.

By default, Visual Studio created some an async method which was throwing errors when I tried to implement it. I just deleted it from the interface and used the standard notification method.

I did this in VS2013 but it should be the same for 2010 and 2012 as well. Make sure you are using 'Add Service Reference' and not 'Add Web Reference'. Don't forget to delete the generated reference as you will not be using it.

-1
votes

WSCF provides a contract-first tool with VS integration.