【问题标题】:Contract-First WCF for Salesforce Outbound Messaging用于 Salesforce 出站消息的合同优先 WCF
【发布时间】:2012-02-07 00:58:17
【问题描述】:

我正在考虑为 Salesforce Outbound Messaging 实施侦听器应用程序。

walk through 使用已弃用的 ASMX Web 服务实现它。代码是使用带有 /serverInterface 开关的 wsdl.exe 生成的。

这是 Salesforce Outbound Messaging 的 wsdl。

<?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 是我需要实现 NotificationBinding 以便 Salesforce 可以在他们的系统上发生事件时调用我的 web 服务。

我已经意识到 svcutil 本身并不支持 Contract-First 开发。

根据Contract-First SOA with WCF,我使用 WSCF.Blue 从 Salesforce wsdl 生成服务器端存根。虽然代码编译我的服务生成的 wsdl 没有所需的通知操作。

我想知道我出了什么问题?


所以我设法使用 wsdl.exe 和 /serverInterface 快速实现 Salesforce wsdl,而且基于 asmx 的应用程序生成的 wsdl 似乎与基于 wcf 的应用程序有很大不同。

这是wsdl.exe用/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);
}

这是 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);
}

它们似乎非常相似,所以我不知道为什么这些应用程序生成的 wsdl 会如此不同?添加 wsdls 是否值得(不想再提出问题了)?

【问题讨论】:

  • 我相信在 WCF 中生成的客户端界面与服务器界面基本相同。
  • 嗯,这对我来说意味着什么?
  • 使用 svcutil 为您的消息传递类生成 shell。无论您是编写客户端还是服务器,它们基本上都是相同的
  • 现在对您不是很有帮助,但 WCF 4.5 将重新引入这个丢失的功能

标签: c# wcf salesforce


【解决方案1】:

您可能不喜欢我的建议,但我相信它实际上是您的最佳选择。使用 ASMX。我有三个 OM 监听器,它们在 4.0 下都可以正常工作

我不知道是谁散布了这些关于 .NET2.0 技术的误解,但其中很多肯定不会仅仅因为开发社区受到新奇迷的困扰而被淘汰; ASMX 就是其中之一(Linq2SQL 等也是)。 WCF 简单地说还没有完成,它不完全符合 WS 标准,这给 salesforce 集成带来了很多麻烦(最痛苦的是 WCF 不支持 salesforce 保存会话信息的soap headers)。

有关为什么 asmx 没有过时的更多信息,请参阅此处:Does .net 4.0 still support asmx

【讨论】:

  • 对于原型应用,我使用了 ASMX。它易于开发且运行良好。
  • 那为什么不将它用于生产级监听器呢?为什么要坚持 WCF?
  • 我们一直使用 WCF,因此为了保持一致性,我们希望将 WCF 用于 OM 侦听器,并且 Microsoft 建议“现在应该使用 Windows Communication Foundation 创建 XML Web 服务和 XML Web 服务客户端”。
  • MS 在问这些问题之前,首先必须学会如何与其他孩子一起玩。否则,该建议变得相当以自我为中心。构建互操作性平台不仅仅是实施最好的标准化技术,而是实施所有这些技术。
  • 虽然您的回答没有回答我的合同优先 WCF 部分问题,但它确实回答了 Salesforce 出站消息部分。此外,您还提供了有关为什么 ASMX 没有过时的详细信息。因此,我决定将赏金奖励给你。
【解决方案2】:

使用 svcutil 生成客户端代理并简单地删除客户端代理实现,并保留服务/数据合同。从客户端/服务器的角度来看,服务合同是对称的。我曾参与过大量以合同为先的项目,发现这已经足够了。

只要确保您拥有所有可供 svcutil 解析的外部 WSDL。我在上面的 WSDL 文本中看到了几个 import 语句。

顺便说一句,不幸的是,WSCF.Blue 并没有完全适应它,因为它已经有几个月没有更新了,而且将来也不太可能更新。可悲的是,主要开发人员去年死于车祸(是的,真的。)

【讨论】:

    【解决方案3】:

    尝试让我的 OBM 侦听器与 WCF 一起工作时,我经历了同样的痛苦。我最初只为通知方法设置了 OperationContract。部署后,我很快意识到需要设置一个数据合约,但由于时间限制,并且不能完全确定这是否是我需要做的全部,我只是切换到一个可以工作的 asmx Web 服务。

    【讨论】:

      【解决方案4】:

      我找到了一种使用内置 Visual Studio 工具从 Salesforce WSDL 生成 WCF 接口的简单方法。我从 Salesforce 下载了 WSDL 并将其保存到我的桌面。在 Visual Studio 中,转到您的一个项目中的添加服务引用菜单(不管在哪里,您都将删除它)。对于 Url,输入您在本地下载的 WSDL 的路径(即 C:\Users\yourusername\Desktop\notification.wsdl)

      这应该会为服务带来数据负载。单击确定以创建引用,现在在服务引用文件夹中,双击您刚刚创建的引用。这应该向您显示对象浏览器,其中您刚刚创建的服务引用的名称空间被突出显示。双击此命名空间内的任何类,它将打开由 Visual Studio 生成的相关 reference.cs。

      将命名空间声明中的所有内容复制到您选择的文件中,使用您决定的任何命名空间。如果您计划处理多个出站消息,您也可以重命名该接口(默认应为 NotificationPort)。该接口将引用您构建它的命名空间,但您可以删除文件中的所有命名空间,因为它所引用的所有类都在同一个文件中。

      创建一个新的 WCF 服务类。删除使用 svc 文件自动创建的接口,并将接口名称更改为您刚刚复制到新文件中的名称。

      默认情况下,Visual Studio 创建了一些异步方法,当我尝试实现它时它会抛出错误。我只是从界面中删除它并使用标准通知方法。

      我在 VS2013 中这样做了,但 2010 年和 2012 年应该也是一样的。确保您使用的是“添加服务参考”而不是“添加 Web 参考”。不要忘记删除生成的引用,因为您不会使用它。

      【讨论】:

        【解决方案5】:

        WSCF 提供具有 VS 集成的合约优先工具。

        【讨论】:

        • 我已经声明 WSCF 不提供来自 Salesforce Outbound Messaging wsdl 的可用服务器端存根。
        • @mob1ljunkie:我觉得你应该将此作为错误报告提交给 wscf.blue 以帮助改进它。它是一个很棒的工具,而且似乎正在为合同优先 SOA 打一场孤军奋战。它需要我们的支持。
        猜你喜欢
        • 1970-01-01
        • 2010-11-06
        • 1970-01-01
        • 2023-03-07
        • 1970-01-01
        • 1970-01-01
        • 2011-10-10
        • 1970-01-01
        • 2010-11-05
        相关资源
        最近更新 更多