Monday, April 15, 2013

Using SalesForce RestAPI VB.Net/C#




SalesForce Tips

SalesForce API calls to fetch data

My exploration on SalesForce APIs had revealed a couple of approaches to fetch data from SalesForce. The main ones are listed below
SOAP Api
Rest calls
Bulk API
Data Loader

Before we begin, there are a set of prerequisites that you need to get done in order to run the sample or interact using the SalesForce API's.

Prerequisites:
User Name  
Password
Security Token (needed for the Api calls as the password is considered as password+security token)
Client Secret (Required for REST Call)
Client Id (Required for REST Call)
Call Back (This is required if we are authenticating from the web, else if we have a console application this could be some dummy value)


You can register for a developer license in case you don't have a user name and password at https://login.salesforce.com/.

Now let's take a look at each of the options available.

SOAP API
This is really an easy and fast way to query data from SalesForce. But is has got some serious drawbacks. Main ones being the limitations in the number of records that are fetched. The current limit is 2000 records per call. You will also need to consider the number of calls that are being made to SalesForce as there is a usage limit for each instance or Org based on the number of requests made. You need to tread with care when you have a large set of data to be retrieved.

The example shown is mainly a console application used to retrieve data from Sales Force

Steps:


1. Create a console application
2. Add reference to SalesForce Service
            You can add reference to the SalesForce service in two ways. Either directly browsing to the wsdl url or download the wsdl and adding it.

Follow the steps shown below to add the reference directly
Click on “Add Service Reference”


Click Advanced

Click “Add Web Reference”


Type the corresponding PARTNER wsdl(my example it was https://na15.salesforce.com/soap/wsdl.jsp) url in the URL and give an appropriate name to your service. Then click “Add Referece”



One important thing to note here is that you need to add a “web reference” itself. If you add a “Service Reference” the proxy class generated from the WSDL is entirely different. 

Where can I get the WSDL?
Once you login to your sales force service (http://login.salesforce.com), navigate to “Set Up” from the user name drop down menu




Click on Develop > API in the left hand navigation



You can see the different options for generating WSDL.
You have either the choice of choosing “Enterprise WSDL” or “Partner WSDL” or “APEX  WSDL”
Enterprise WSDL is useful when you want to have a proxy class that is tightly coupled to the schema you have built in sales force. You need to re-add the WSDL if you have changed the schema in sales force.
e.g. Dim account As Account = CType (qr.records(i), Account)
You can work directly with the Account entity and its fields if you have a reference to Enterprise WSDL.
The more generic one is the Partner WSDL,which is a loosely typed association. Here you will need to work on the generic object sObject to go through the data.
e.g Dim record As sForceService.sObject = qr.records(i)

3. Login to salesForce service:

You will need username and password. Password comprises of password + the security token. So if your password is “abcd” and token is “EFGH” then the password to be passed to the service is “abcdEFGH”.

' Create the binding to the sforce servics
 bindingService = New sForceService.SforceService

 ' Set the time out valie
 bindingService.Timeout = 60000

Dim loginRes As sForceService.LoginResult = bindingService.login(userName, password)

4. Assign the session Id to the SOAP header for subsequent calls:

This is required if you need to make further calls to the API service.
'Create a new session header object and set the session id to that returned by the login
 bindingService.SessionHeaderValue = New sForceService.SessionHeader
bindingService.SessionHeaderValue.sessionId = loginRes.sessionId


5. Get the new url for you org:

Once the login succeeds you need to change the endpoint url of you service to the new value returned after successful login.

'Change the binding to the new endpoint
bindingService.Url = loginRes.serverUrl

Initialize the forceService object. Execute the query method of the SforceService object.

'Set the query option, batch size (maximum value allowed is 2000, beyond this value it
'gives and INVALID_BATCH_SIZE error;
Dim results As sForceService.QueryResult = Nothing
bindingService.QueryOptionsValue = New sForceService.QueryOptions
bindingService.QueryOptionsValue.batchSize = 2000
bindingService.QueryOptionsValue.batchSizeSpecified = True

'Run the qquery
 results = bindingService.query("select FirstName, LastName from Contact")


Once you get the results you get loop through the results and get the column values.

For i As Integer = 0 To results.records.GetUpperBound(0)

    Dim record As sForceService.sObject = results.records(i)

Next

You will need to check if there are more results to be fetched. In that case you need to call the queryMore option with the current location of the returned results. The queryMore picks up the last record id and does a new fetch from that position.

If results.done Then
              bContinue = False
       Else
              results = bindingService.queryMore(qr.queryLocator)
End If

6. Done

Below is the required functions needed to call using the SOAP API

'      /*
    '* login sample
    '* Prompts for username and password, set class variable binding
    '* resets the url for the binding and adds the session header to
    '* the binding class variable
    '*/
    Private Function login() As Boolean

        Console.Write("Enter user name: ")
        Dim userName As String = Console.ReadLine()
        If userName.Length = 0 Then
            Return False
        End If

        Console.Write("Enter password: ")
        Dim password As String = Console.ReadLine()

        If password.Length = 0 Then
            Return False
        End If

        '//Provide feed back while we create the web service binding
        Console.WriteLine("Creating the binding to the web service...")

        ' Create the binding to the sforce servics
        bindingService = New sForceService.SforceService

        ' Set the time out valie
        bindingService.Timeout = 60000

        '//Attempt the login giving the user feedback
        Console.WriteLine("LOGGING IN NOW....")

        Try
            Dim loginRes As sForceService.LoginResult = bindingService.login(userName, password)
        Catch e As System.Web.Services.Protocols.SoapException
            '// This is likley to be caused by bad username or password
            Console.Write(e.Message & ", please try again." & vbCrLf & vbCrLf & "Hit return to continue...")
            Console.ReadLine()
            Return False
        Catch ex As Exception
            '// This is something else, probably comminication
            Console.Write(ex.Message & ", please try again." & vbCrLf & vbCrLf & "Hit return to continue...")
            Console.ReadLine()
            Return False
        End Try

        Console.WriteLine(vbCrLf & "The session id is: " & loginRes.sessionId)
        Console.WriteLine(vbCrLf & "The new server url is: " & loginRes.serverUrl)

        'Change the binding to the new endpoint
        bindingService.Url = loginRes.serverUrl

        'Create a new session header object and set the session id to that returned by the login
        bindingService.SessionHeaderValue = New sForceService.SessionHeader
        bindingService.SessionHeaderValue.sessionId = loginRes.sessionId
        Debug.WriteLine(loginRes.sessionId)
        loggedIn = True

        Return True

    End Function


Private Sub querySample()
        'Verify that we are already authenticated, if not
        'call the login function to do so
        If Not loggedIn Then
            If Not login() Then
                Return
            End If
        End If

        'Set the query option, batch size (maximum value allowed is 2000, beyond this value it
        'gives and INVALID_BATCH_SIZE error;
        Dim results As sForceService.QueryResult = Nothing
        bindingService.QueryOptionsValue = New sForceService.QueryOptions
        bindingService.QueryOptionsValue.batchSize = 2000
        bindingService.QueryOptionsValue.batchSizeSpecified = True

        Try
            'Run the qquery
            results = bindingService.query("select FirstName, LastName from Contact")
           
            'create a looop control variable for the loop & 1 behavior
            Dim bContinue As Boolean = True
            Dim loopCounter As Integer = 0
            While bContinue
                'process the query results
                loopCounter += 1
                Console.WriteLine(vbCrLf & "Results Set " &Convert.ToString(loopCounter) & " - ")
                For i As Integer = 0 To results.records.GetUpperBound(0)

                    Dim record As sForceService.sObject = results.records(i)
                    Dim fName As String = getFieldValue("FirstName", record.Any)
                    Dim lName As String = getFieldValue("LastName", record.Any)
                    If fName Is Nothing Then
                        Console.WriteLine("Contact " & (i + 1) & ": " & lName)
                    Else
                        Console.WriteLine("Contact " & (i + 1) & ": " & fName & " " & lName)
                    End If
                Next
                'handle the loop + 1 problem by checking to see if the most recent queryResult
                'set is
                If results.done Then
                    bContinue = False
                Else
                    results = bindingService.queryMore(results.queryLocator)
                End If
            End While
            Console.WriteLine(vbCrLf & "Query succesfully executed.")
            Console.Write(vbCrLf & "Hit return to continue...")
            Console.ReadLine()
        Catch ex As Exception
            Console.WriteLine(vbCrLf & "Failed to execute query succesfully, error message was: " & vbCrLf & ex.Message)
            Console.Write(vbCrLf & "Hit return to continue...")
            Console.ReadLine()
        End Try


Private Function getFieldValue(ByVal fieldName As StringByVal fields() AsSystem.Xml.XmlElementAs String

        Dim returnValue As String = ""
        If Not fields Is Nothing Then
            For i As Integer = 0 To fields.GetUpperBound(0)
                If fields(i).LocalName.ToLower().Equals(fieldName.ToLower()) Then
                    returnValue = fields(i).InnerText
                End If
            Next
        End If
        Return returnValue

    End Function


Thats all for the call using SOAP API. Watch this space for updates on REST and the BULK API.
Hope this helps !!!

6 comments:

  1. So how about that REST and Bulk API?

    ReplyDelete
    Replies
    1. Was stuck up with quite a few things ..hence the delay..
      Check out the two links
      rest api - http://integratesalesforce.blogspot.ch/2013/06/salesforce-rest-api-sample-using-vbnet.html

      bulk Api- http://integratesalesforce.blogspot.ch/2013/06/salesforce-using-bulk-api-sample-vbnet.html

      Delete
  2. The above example was very helpful..
    Can you show some examples for update records Enterprises WSDL using vb.net please.........

    ReplyDelete
    Replies
    1. Hi SJohn,

      Enterprise WSDL is quite similar, though I have not tried this out.
      The main advantage here is that its more typed,in the sense that if you have some custom entities you will be able to get the results and cast it to the right object.
      In the above example, instead of the below line
      Dim record As sForceService.sObject = results.records(i)

      You could directly get it as an Account object for example
      Dim account As sForceService.Account = results.records(i)
      Then you would be able to use the fields directly -
      account.Name etc.

      Hope this helps!!

      Delete
  3. Hi Ashwin,

    Thanks for your help. I have tried the below and it's worked


    Dim updateAccount As New sForceService.Case
    updateAccount.Id =
    updateAccount.FulfilmentStatus__c =
    Dim saveResults() As sForceService.SaveResult = bindingService.update(New sForceService.Case() {updateAccount})
    updateAccount = Nothing

    Thanks
    SJohn

    ReplyDelete
  4. This blog is awesome! Extremely well laid out tutorial; not quite what I was looking for (enterprise wsdl C# example), but I can appreciate what you've done here.

    ReplyDelete