DEV Community

loading...

Updating a Record Using C# .NET and the Salesforce REST API

Rachel Soderberg
I'm a Software Developer who loves working with C#.NET and Salesforce. In my free time I lift weights, do martial arts, and play video games.
Updated on ・3 min read

This article is a continuation of the Salesforce REST API Services series. In this article we are operating under the assumption that you have already set up your Salesforce org and built an application that successfully authenticates with Salesforce and logs in. The steps in this tutorial will not work without the resulting AuthToken from your Salesforce org. Ideally, your application also retrieves an AccountId, but this can be passed as a string as well.

In today's tutorial we're going to perform an update to an Account record in our Salesforce org. Updating a record from a C# application is useful in cases where records must be updated automatically on an object-by-object basis, such as modifying orders, updating customer records, or setting a custom status after completing a task. Your application can submit an update with all of the info from your use case and in moments your Salesforce record will reflect the new values.

Performing an Update

As suggested in part 2 of this series, I suggest creating a generic UpdateRecord() method as this code can potentially be reused dozens of times in a code base. This is going to look very similar to the QueryRecord() method we created in the last step, but there are some differences such as the use of HttpContent used to represent our HTTP entity body and content headers, where we'll pass in our encoded XML message.

Also similarly to a query, we are using our API Endpoint, Service URL, and AuthToken to make and confirm a successful connection with our Salesforce org.

    private string UpdateRecord(HttpClient client, string updateMessage, string recordType, string recordId)
    {
        HttpContent contentUpdate = new StringContent(updateMessage, Encoding.UTF8, "application/xml");

        string uri = $"{ServiceUrl}{ApiEndpoint}sobjects/{recordType}/{recordId}?_HttpMethod=PATCH";

        HttpRequestMessage requestUpdate = new HttpRequestMessage(HttpMethod.Post, uri);
        requestUpdate.Headers.Add("Authorization", "Bearer " + AuthToken);
        requestUpdate.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));
        requestUpdate.Content = contentUpdate;

        HttpResponseMessage response = client.SendAsync(requestUpdate).Result;
        return response.Content.ReadAsStringAsync().Result;
    }

Now let's say we're writing a service that updates a customer's phone and mobile numbers on their Account record.

    public string UpdateCustomerAccountPhoneNumbers()
    {
        string phone = "123-123-1234";
        string mobile = "456-456-4567";

        string updateMessage = $"<root>" +
            $"<Phone>{phone}</Phone>" +
            $"<Phone2__c>{mobile}</Phone2__c>" +
            $"</root>";

        string result = UpdateRecord(Client, updateMessage, "Account", accountId);

        if (result != "")
        {
            logger.SalesforceError("Update", "Account");
            return null;
        }

        logger.SalesforceSuccess("Update", "Account", accountId);
        return accountId;
    }

I've included the error handling in this example as well because one way you can confirm you've actually updated the record successfully is when you're returned an empty result string in debug mode. If anything went wrong, such as an invalid field name being used, you will get back an error message to help you resolve the issue. Let's say I tried to do 123-456-7890 in the above example. The update would fail and I would receive this response:

"<?xml version=\"1.0\" encoding=\"UTF-8\"?>INVALID_FIELDNo such column 'PhoneNumber' on entity 'Account'. If you are attempting to use a custom field, be sure to append the '__c' after the custom field name. Please reference your WSDL or the describe call for the appropriate names."

With an update result of "" you can be certain your record was updated correctly and you can continue on with further functionality.

Next in this series we will cover the creation of a new Account record, which is helpful when our query method results in no records found.

--

If you'd like to catch up with me on social media, come find me over on Twitter or LinkedIn and say hello!

Discussion (6)

Collapse
rickystam profile image
Ricky Stam

First of all, thank you for the nice article!
I am curious on why you need to check for the empty string for success, wouldn't checking the Response HTTP Status Code enough? My expectation would be that for success the response would be 200 or 204 and in case of failure 500, 400 or something else that indicates failure.

Collapse
rachelsoderberg profile image
Rachel Soderberg Author

That's a great idea, I'm going to look into this! I'm still learning as I go, and this was the best method I came up with as I am also new to working with web development in general, so I appreciate the pointer. This actually sounds like a cleaner way to do it.

Collapse
yucked profile image
Yucked

Shouldn't there be a single HttpClient that lasts throughout an applications lifetime?

Collapse
rachelsoderberg profile image
Rachel Soderberg Author • Edited

That's a great question, and I'll be looking into that today because I'm new to web development and RESTful services and learning as I go. If I come up with some good info either way I'll get back to you!

Collapse
rachelsoderberg profile image
Rachel Soderberg Author • Edited

You are absolutely right! I never ran into issues with exhausting the sockets because of how small my testing domain has been so far, but I can see how this would have bit me down the road in our production environment. So you not only saved me a headache later down the road, but I've updated my examples as well. Thank you friend!

In case anyone else is curious, here's what I learned!

HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads. This will result in SocketException errors.

Collapse
yucked profile image
Yucked

Cheers. Another thing I noticed. .Result can in some cases cause deadlocks, iirc. I would change method signature to asynchronous Task and await everything within methods body. Along with wrapping HttpRequestMessage in a using statement or simply using var request Update = ....