3

I am experimenting with creating a SOAP connection to Salesforce using C#. I am using the sample code in SOAP API Developer Guide (PDF pages 22-28). It says it uses "A .NET 3.0 style reference uses services like SoapClient". I assume it was referring to the SOAPClient in Microsoft.Web.Services3. When I try to instance a SoapClient it gives the error "can't create an instance from an abstract class or interface". I added a Service Reference to the WSDL file and that worked.

Questions:

  • Does anyone have any ideas on why it can't instance it? (Docs show it should work)
  • Does anyone have this example working? Can you share it?

Thanks.

Code:

using System;
using System.ServiceModel;
using Microsoft.Web.Services3.Messaging;
using SimpleConnect.SFProd;  // WSDL XML File

namespace SimpleConnect
{
class SimpleConnect
{
    private static SoapClient loginClient; // for login endpoint
....
    static void Main(string[] args)
    {
        SimpleConnect sample = new SimpleConnect();
......
        loginClient = new SoapClient();    //************************** Error
  • I don't know the answer. But I saw that the SoapClient class implemented in the Microsoft.Web.Services3.Messaging namespace is an abstract class. So it's normal that you can't instanciate it. Probably, the wsdl provided by Salesforce declare its own SoapClient class. – Martin Lezer Jan 12 '17 at 16:47
  • Guys, I don't see how this is related to Salesforce. Did you accidentally post in the wrong forum? – Sebastian Kessel Jan 12 '17 at 18:11
  • 1
    The document and example I posted is directly from the Salesforce SOAP API Developer Guide. Can you be specific on why a question regarding a Salesforce provided document and example is off-topic. – user2970483 Jan 12 '17 at 21:40
  • 1
    Yes, the Salesforce documentation in Step 3: Import the WSDL File Into Your Development Platform is confusing here. They don't mean add a using to import Microsoft.Web.Services3.Messaging. Instead they are referencing the different output between adding a Web Reference and a Service Reference. – Daniel Ballinger Jan 24 '17 at 20:32
  • If we can get this question reopened for you I'll give a more complete answer. In the short term you might find the Nothing But .Net blog series useful. In particular, have a look at the Force.com Toolkit for .NET if you want to use the REST API rather than the SOAP API. – Daniel Ballinger Jan 24 '17 at 20:35
1

The key is in the previous step of the SalesForce SOAP API Quick Start.

You can add either a .NET 2.0 style Web reference, or a .NET 3.0 style Service reference, depending on your version of Visual Studio and preferred developer environment. A .NET 3.0 style reference uses services like SoapClient instead of SforceService. (emphasis mine)

The following section of that page details adding a web reference, but offers no guidance for adding a service reference. It's even easier than adding a web reference.

1. Generate Your WDSL

Browse to your Salesforce.com instance and generate a WSDL file (same as in the Salesforce docs).

generate Salesforce WSDL

2. Add a Service Reference

I'm using Visual Studio 2017. This is what the process looks like for me.

Visual Studio add service reference dropdown

Note that I had to set the namespace to "sForce".

Visual Studio add service reference dialog

3. Add a Using Statement

The namespace of the Salesforce reference is placed inside the project reference. As you can see, there's no compile error for my reference to SoapClient, which was provided in the sForce namespace.

using the Salesforce reference namespace

Since the documentation gives example code for the newer service reference, it should really include instructions to set up the service reference. But hey, they're pretty good docs overall, so I guess it's fine.

Do note that there's a performance impact to using the service instead of the web reference.

Also note I had to use a workaround to avoid a bug in how the reference is generated from WSDL. It's described quite well in another answer.

4. Use the Service

Once the service is set up, you can use code from the Salesforce tutorial to log in (copied here with slight modifications):

public class SfSession
{
    public static SoapClient client;
    private static SoapClient loginClient;
    private static SessionHeader sessionHeader;
    private static EndpointAddress endpoint;

    public bool Login()
    {
        string username = "<EMAIL>";
        string password = "<PASSWORD>(<TOKEN>)";

        loginClient = new SoapClient();

        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
        LoginResult lr = loginClient.login(null, username, password);

        endpoint = new EndpointAddress(lr.serverUrl);

        sessionHeader = new SessionHeader
        {
            sessionId = lr.sessionId
        };

        client = new SoapClient("Soap", endpoint);

        return true;
    }

    private void Logout()
    {
        client.logout(sessionHeader);
    }
}

(btw, don't leave your username/password in code... I put it there solely for testing)

  • Thanks!!! I was able to get it work using the workaround and a Web Service Reference but I couldn't get it to work using Service Reference. Could you show the code for a login? – user2970483 Aug 23 '17 at 22:25
  • @user2970483, just added example code. – Michael Crenshaw Aug 24 '17 at 13:39
  • 1
    Thanks for this. I actually got it working yesterday with .Net as a test. I'm trying to get it working using .Net Core and it uses a Service Reference as well Microsoft WCF Web Service Reference. It is async. I just posted on it .Net Core 2.0 and async/await Usage. – user2970483 Aug 25 '17 at 15:15
2

C# has a SOAP bug with a WSDL. I vaguely remember that everything needs an attribute being the cause. Bug information is listed below. You can edit your WSDL to the following to fix the issue:

Before:

<complexType name="ListViewRecord">
  <sequence>
   <element name="columns" type="tns:ListViewRecordColumn" maxOccurs="unbounded"/>
  </sequence>
</complexType>

<complexType name="ListViewRecordColumn">
<sequence>
  <element name="fieldNameOrPath" type="xsd:string"/>
  <element name="value" type="xsd:string" nillable="true"/>
</sequence>
</complexType>

After:

<complexType name="ListViewRecord">
  <sequence>
   <element name="columns" type="tns:ListViewRecordColumn" maxOccurs="unbounded"/>
  </sequence>
  <xsd:attribute name="tmp" type="xsd:string" />
</complexType>

<complexType name="ListViewRecordColumn">
<sequence>
  <element name="fieldNameOrPath" type="xsd:string"/>
  <element name="value" type="xsd:string" nillable="true"/>
</sequence>
<xsd:attribute name="tmp" type="xsd:string" />
</complexType>

Update:

Found the Bug Information:

XmlSerializer Code Generation component cannot handle nested unbounded elements when there is only one element

Salesforce Support on the Issue

  • Thanks for this. This hung me up 6 months ago and decided to the Soap Office Toolkit and Excel/VBA for reporting. I would like a Linux server approach using EPPlus using .Net Core 2.0. – user2970483 Aug 23 '17 at 22:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.