September 18, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Salesforce Integration with .Net Web Services SOAP API

  • March 15, 2011
  • By Darren Terrell
  • Send Email »
  • More Articles »

Introduction

Salesforce is the top rated sales application in the world today. This success is greatly attributed to the built-in CRM functionality, a plethora of extensibility options, and ease of use. Since the multitenant platform is cloud-based, companies can focus on their core business and not worry about maintaining an elastic and dependable infrastructure.

One key feature of Salesforce is its straightforward manner of extending functionality. Business power users can create new data storage objects, pages, views and reports to support their business. As you can imagine, this will leave vital data and business logic in the cloud. So, there will often be times when a company will need to integrate its existing enterprise systems with the Salesforce platform. Thus, the focus of this article will be on the most common way to connect .Net applications to Salesforce for data retrieval and execution of application logic. This method is the Web Services SOAP API.

Web Services SOAP API

The first aspect of integration with the Salesforce that will be looked at is the Salesforce Web Services SOAP API. The services are comprised of various WSDLs, listed in Figure 1, that allow for manipulating data, creating objects, generating code, and viewing metadata just to name a few. You can find the API WSDLs located in Salesforce by going to "Setup > Develop > API." If you do not have a Force.com account, then you can create a free one. With so much functionality available in the API, this glance will be limited to core functionality in the Enterprise WDSL. This WSDL is a strongly typed representation of an organizations metadata. This means that sObjects, such as Account and Contact, will be available as classes in .Net.

Figure 1

Figure - API WSDLs in Salesforce

Let’s start exploring the SOAP API functionality by creating a sample .Net application and connect into Salesforce. Here are the steps that can be taken to setup the Enterprise WSDL:

  1. Create an empty ASP.Net MVC application.
  2. Navigate to the API page and click on the link "Generate Enterprise WSDL." This will build a WDSL from your organization’s current metadata and make it available for download. Create a folder in the root of the web application and save the WSDL download into it.
  3. Create a Web Reference to the WSDL by specifying the path to the download file. I named my reference SalesforceEnterprise in the example project. Here is what a local file system path will look like (notice the file:/// prefix): file:///C:\Code\Salesforce\NetToSalesforce\Wsdls\Enterprise.wsdl
  4. The strongly typed WSDL will generate classes that represent the sObjects when the reference is created. Therefore, it will require the reference to be updated anytime the referenced WSDL is altered.
  5. If you download the code with this article, make sure to update the web.config connection string to include your username, password, and security token.

Building the Connection

Now that the base project is created and a reference setup to the Enterprise WSDL, we can transition to writing code that calls the API. Refer to the sequence diagram in Figure 2 as an example of the flow of making a Salesforce request. The first step is to have Salesforce authenticate us as a user. The Enterprise and Partner WSDLs contain definitions to allow for authentication for outside applications to integrate with Salesforce. Once the authentication with Salesforce is successful, additional authentication information, such as the Session ID and URL will be returned. This information is needed for subsequent API calls. The Session ID can be cached in your application for periods less than the timeout period. Go to "Setup > Security Controls > Session Settings" to see what your timeout is. For this sample, caching was omitted to keep things simple. Here is example code showing how to authenticate against Salesforce:

public class ForceConnection
{
    public string SessionID { get; set; }
    public string ServerUrl { get; set; }

    public ForceConnection(string connectionString)
    {
        ForceConnectionStringBuilder connectionBuilder = 
            new ForceConnectionStringBuilder(connectionString);

        Login(connectionBuilder.Username, connectionBuilder.Password, connectionBuilder.Token);
    }

    public ForceConnection(string username, string password, string securityToken)
    {
        Login(username, password, securityToken);
    }

    private bool Login(string username, string password, string securityToken)
    {
        try
        {
            using (SforceService service = new SforceService())
            {
                LoginResult loginResult = 
                    service.login(username, String.Concat(password, securityToken));

                this.SessionID = loginResult.sessionId;
                this.ServerUrl = loginResult.serverUrl;
            }

            return true;
        }
        catch (Exception)
        {
            return false;
        }
    }
} 

To authenticate, we can to call the "login" method on an instance of the SforceService while passing the username and password. The password needs to have the security token appended on the end. If you do not know what your token is, then go to "Setup > My Personal Information > Reset My Security Token." After the login is successful, the Session ID and URL values retrieve from the service are stored in properties on the ForceConnection class. Again, I highly recommend caching these calls to increase application performance.

Figure 2

Figure - Sequence Diagram API Request Flow

Making the CRUDy Calls

After the authentication is complete, we can make a query to the API using the Session ID and URL. In the ApiService wrapper class below, there are example calls to the API.

ApiService.cs Class

public class ApiService : IDisposable
{
    public static Dictionary<Guid, List<sObject>> asyncResults;

    private SforceService salesforceService;
    const int defaultTimeout = 30000;

    public ApiService()
    {
        salesforceService = new SforceService();
        salesforceService.Timeout = defaultTimeout;
        asyncResults = new Dictionary<Guid, List<sObject>>();
    }

    public ApiService(int timeout) : this()
    {
        salesforceService.Timeout = timeout;
    }

    public List<T> Query<T>(string soql) where T : sObject, new()
    {
        List<T> returnList = new List<T>();

        SetupService();

        QueryResult results = salesforceService.query(soql);

        for (int i = 0; i < results.size; i++)
        {
            T item = results.records[i] as T;

            if (item != null)
                returnList.Add(item);
        }

        return returnList;
    }

    public T QuerySingle<T>(string soql) where T : sObject, new()
    {
        T returnValue = new T();

        SetupService();

        QueryResult results = salesforceService.query(soql);

        if (results.size == 1)
            returnValue = results.records[0] as T;

        return returnValue;
    }

    public Guid QueryAsync(string soql)
    {
        SetupService();
        salesforceService.queryCompleted += salesforceService_queryCompleted;
        
        Guid id = Guid.NewGuid();

        salesforceService.queryAsync(soql, id);

        return id;
    }

    void salesforceService_queryCompleted(object sender, queryCompletedEventArgs e)
    {
        Guid id = (Guid)e.UserState;
        List<sObject> results = e.Result.records.ToList();

        if (asyncResults.ContainsKey(id))
            asyncResults[id].AddRange(results);
        else
            asyncResults.Add((Guid)e.UserState, results);
    }

    public SaveResult[] Update(sObject[] items)
    {
        SetupService();

        return salesforceService.update(items);
    }

    public UpsertResult[] Upsert(string externalID, sObject[] items)
    {
        SetupService();

        return salesforceService.upsert(externalID, items);
    }

    public SaveResult[] Insert(sObject[] items)
    {
        SetupService();

        return salesforceService.create(items);
    }

    public DeleteResult[] Delete(string[] ids)
    {
        SetupService();

        return salesforceService.delete(ids);
    }

    public UndeleteResult[] Undelete(string[] ids)
    {
        SetupService();

        return salesforceService.undelete(ids);
    }

    private void SetupService()
    {
        ForceConnection connection = new ForceConnection("SalesforceLogin");
        salesforceService.SessionHeaderValue =
            new SessionHeader() { sessionId = connection.SessionID };

        salesforceService.Url = connection.ServerUrl;
    }

    public void  Dispose()
    {
        salesforceService.Dispose();
    }
}

You will notice after the connection Session ID and URL are returned, they are set to corresponding properties on an instance of the service. The Session ID is a security token that lets Salesforce know the request has been authenticated already. The URL is a web address to the API web services API.

Now that the service is setup, a query can be made to Salesforce. In the code above, a Saleforce Object Query Language (SOQL) statement is passed to the API defining the query. SOQL is very similar to SQL in structure and behavior. You can learn more about SOQL by going to Salesforce.com. In Apex code, it can be used in a manner similar to LINQ (in Eclipse). Once the statement is processed, the API will return the query results as a collection of sObjects. As depicted in the List action of the Api controller, this data can be used like any other objects in .Net. In this case, the Contact sObject is passed as the Model to the view.

ApiController.cs Controller Excerpt

public ActionResult List()
{
    string soql =
       @"SELECT c.ID, c.Email, c.FirstName, c.LastName, c.Name
         FROM Contact c
         WHERE c.Email &lt;&gt; ''";

    using (ApiService api = new ApiService())
    {
        List<Contact> contacts = api.Query<Contact&gt;(soql);

        return View(contacts);
    }
}

<H2>List.aspx View Excerpt</H2>
<html>
<head runat="server">
    <title>List Contacts (Web Service API)</title>
</head>
<body>
    <div>
        <p><a href="/">Return Home</a></p>
    </div>
    <div>
        <p>Contacts</p>
        <ul style="list-style-type: none;">
    <%
        foreach (NetToSalesforce.Api.Contact c in Model)
        {
            Response.Write(
                String.Format("<li><a href='/api/edit/{0}'>{1}</a> {2} </li>", 
                    c.Id, 
                    c.Name, 
                    c.Email));
        }
    %>
        </ul>
    </div>
</body>
</html>

Not only can outside applications read data using the Web Service API, they can also manipulate it via: inserts, updates, upserts, deletes, and undeletes. The next example will be for updating an email address for a Contact.

ApiController.cs Controller Excerpt

public ActionResult Update(string id, string email)
{
    Contact modifiedContact = new Contact() { Id = id, Email = email };
    
    // To reset a field to null, the field name needs to be added to the 
    // fieldsToNull array.  The line below is an example of how to do this.
    //modifiedContact.fieldsToNull = new string[] { "FirstName", "LastName" };

    using (ApiService api = new ApiService())
    {
        sObject[] updates = new sObject[] { modifiedContact };
        api.Update(updates);
    }

    return RedirectToAction("List");
}

ApiService.cs Excerpt
    public SaveResult[] Update(sObject[] items)
    {
        SetupService();

        return salesforceService.update(items);
    }

When the sample web application is run, you will see a list of contacts pulled back using the API. When one is selected, you will be taken to an edit page where the email address can be updated. As illustrated in the code above, a Contact instance is created and the Id and Email properties are set in the controller. The Id property is a required field for the update because it is unique across all sObjects in the cloud. This allows Salesforce to know that you are updating an existing object instead of creating a new one. All properties that have values assigned will be updated in Salesforce. However, those that contain a null value will not be changed. To change a property with a value to null, you need to explicitly add the field name to the fieldsToNull array property. An example of this is commented out in the controller Update method. From this point, all we need to do is pass the Contact to the update method of the API Service. The changes will be persisted in Salesforce. You can see this by navigating to the contacts page in Salesforce. The rest of the CRUD calls work in a similar fashion except the asynchronous calls.

Asynchronous Calls

A nice feature built into the SOAP API is the ability to call the service methods asynchronously. To do this we will use our QueryAsync method on our ApiService wrapper class. This method generates a token id that can be used to reference the results in a collection. When the query is complete, the queryCompleted event will be raised. In our example, we use this event to grab our results and store them in a repository in memory via a static variable. Those results can later be retrieved by a polling application. To illustrate this, refer to the example code below:

ApiController.cs Controller Excerpt

public JsonResult StartQuery()
{
    Guid id;
    string soql =
       @"SELECT c.ID, c.Email, c.FirstName, c.LastName, c.Name
         FROM Contact c";

    using (ApiService api = new ApiService())
    {
        id = api.QueryAsync(soql);

        return Json(new { id = id });
    }
}

public JsonResult GetQuery(Guid id)
{
    bool success = ApiService.asyncResults.ContainsKey(id);
    List<Contact> contacts = new List<Contact>();

    if (success)
    {
        ApiService.asyncResults[id].ForEach(i => contacts.Add(i as Contact));
        ApiService.asyncResults.Remove(id);
    }

    return Json(new { success = success, contacts = contacts });
}

ListAsync.aspx View

<html>
<head runat="server">
    <title>List Contacts Async</title>
    <script src="/Scripts/jquery-1.4.4.js" type="text/javascript"></script>
    <script type="text/javascript">
        function startQuery() {
            if ($("#ID").val() == "") {
                $.ajax({
                    type: "POST",
                    url: "/Api/StartQuery",
                    dataType: "json",
                    success: function (result) { $("#ID").val(result.id) }
                });
            }
        }

        function getQuery() {

            if ($("#ID").val() != "") {
                var id = { "id": $("#ID").val() };

                $.ajax({
                    type: "POST",
                    url: "/Api/GetQuery",
                    data: id,
                    dataType: "json",
                    success: function (result) {
                        $("#Contacts").empty();
                        $("#Contacts").append("<p>Contacts</p>");
                        var text = "<ul style='list-style-type: none;'>";

                        for (var i = 0; i < result.contacts.length; i++) {
                            text = text + "<li>";
                            text = text + result.contacts[i].FirstName + " ";
                            text = text + result.contacts[i].LastName + " (";
                            text = text + result.contacts[i].Email + ")";
                            text = text + "</li>";
                        }

                        $("#Contacts").append(text + "</ul>");
                    }
                });
            }
        }
    </script>
</head>
<body>
    <div id="Contacts">
        No contacts listed.
        <br />
    </div>
    <div>
        <input type="hidden" id="ID" />
        <button id="StartButton" onclick="startQuery()">Start Query</button>
        <button id="GetButton" onclick="getQuery()">Get Results</button>
    </div>
</body>
</html>

ApiService.cs Excerpt

public Guid QueryAsync(string soql)
{
    SetupService();
    salesforceService.queryCompleted += salesforceService_queryCompleted;
    
    Guid id = Guid.NewGuid();

    salesforceService.queryAsync(soql, id);

    return id;
}

void salesforceService_queryCompleted(object sender, queryCompletedEventArgs e)
{
    Guid id = (Guid)e.UserState;
    List<sObject> results = e.Result.records.ToList();

    if (asyncResults.ContainsKey(id))
        asyncResults[id].AddRange(results);
    else
        asyncResults.Add((Guid)e.UserState, results);
}

The ListAsync view on the ApiController has basically a blank page with two buttons. The Start Query button calls the controller using AJAX to start the asynchronous query. The reference token is returned to the view and stored in a hidden field. Clicking on the Get Query button attempts to retrieve any results for that token and will display them on the page.

Conclusion

As you can see, the Salesforce Web Service API is very robust. There are many other features that were not been covered by this glimpse at the API. However, the core functionality has been demonstrated and there is abundant documentation at Force.com. Along with the standard functionality, this article has also walked through the more advanced feature of asynchronous calls.

As more companies use the Salesforce cloud, the demand to connect to it from other systems will increase. Companies will need to integrate with Salesforce to enhance their lines of business.


Tags: Microsoft, Salesforce, tutorial, SOAP API, .Net Web Services

Originally published on http://www.developer.com.


Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel