3

When sending the following XML and CSV, I get this error on the Bulk Data Load Job Detail screen:

Got an unexpected error while processing BULK-API. Contact support with error ID: 1070223644-7484 (-43640737)

Job Info (XML):

<jobInfo xmlns="http://www.force.com/2009/06/asyncapi/dataload">
  <operation>upsert</operation>
  <object>contact</object>
  <externalIdFieldName>CustomId__c</externalIdFieldName>
  <contentType>CSV</contentType>
</jobInfo>

Data (CSV):

FirstName,LastName,Department,Birthdate,Description,CustomId__c
"Tom","Jones","Marketing","1940-06-07Z","Self-described as ""the top"" branding
guru on the West Coast","1000"
"Ian","Durys","R&D","","World-renowned expert in fuzzy logic design. Influential
 in technology purchases.","1001"

I have tried sending with and without the ID column. But, I get the same error. This is how my custom field is set up:

CustomId   CustomId__c   Number(18, 0) (External ID) (Unique)

How can I properly perform an upsert with this simple example data?

2

1 Answer 1

3

I created the following class which I am posting to help others:

class SalesForceBulkApi
{

    public static string UserName;
    public static string Password;
    public static SforceService SfdcBinding = null;
    public static LoginResult CurrentLoginResult = null;
    public static string operation = string.Empty;
    public static string serverUrl = string.Empty;

    static SalesForceBulkApi()
    {
        UserName = "YourUserName";
        Password = "YourPassWord";

        serverUrl = "https://naYOUR_SERVER_NUMBER.salesforce.com/services/async/28.0";

        SfdcBinding = new SforceService();

        try
        {
            CurrentLoginResult = SfdcBinding.login(UserName, Password);
        }
        catch (System.Web.Services.Protocols.SoapException e)
        {
            // This is likley to be caused by bad username or password
            SfdcBinding = null;
            throw (e);
        }
        catch (Exception e)
        {
            // This is something else, probably comminication
            SfdcBinding = null;
            throw (e);
        }

        //Change the binding to the new endpoint
        SfdcBinding.Url = CurrentLoginResult.serverUrl;

        //Create a new session header object and set the session id to that returned by the login
        SfdcBinding.SessionHeaderValue = new SessionHeader();
        SfdcBinding.SessionHeaderValue.sessionId = CurrentLoginResult.sessionId;
    }

    public static JobInfo CreateJob(string _operation, string objectType, string externalIdFieldName = null)
    {
        _operation = _operation.ToLower();

        operation = _operation;

        XNamespace dl = "http://www.force.com/2009/06/asyncapi/dataload";
        XElement jobInfo;

        if (externalIdFieldName == null)
        {
            jobInfo = new XElement(dl + "jobInfo",
                new XElement(dl + "operation", operation),
                new XElement(dl + "object", objectType),
                new XElement(dl + "contentType", "CSV")
            );
        }
        else
        {
            jobInfo = new XElement(dl + "jobInfo",
                new XElement(dl + "operation", operation),
                new XElement(dl + "object", objectType),
                new XElement(dl + "externalIdFieldName", externalIdFieldName),
                new XElement(dl + "contentType", "CSV")
            );
        }

        Console.WriteLine(jobInfo.ToString());

        string url = serverUrl + "/job";

        string response = WebRequestPostData(url, jobInfo.ToString(), SfdcBinding.SessionHeaderValue.sessionId, "application/xml");

        // parse response to get the job ID

        var serializer = new XmlSerializer(typeof(JobInfo));

        JobInfo result;

        using (var stream = new StringReader(response))
        using (var reader = XmlReader.Create(stream))
        {
            result = (JobInfo)serializer.Deserialize(reader);
        }

        return result;
    }

    public static JobInfo CloseJob(string jobId)
    {
        XNamespace dl = "http://www.force.com/2009/06/asyncapi/dataload";
        XElement jobInfo = new XElement(dl + "jobInfo",
            new XElement(dl + "state", "Closed")
        );

        Console.WriteLine(jobInfo.ToString());

        string url = serverUrl + "/job/" + jobId;

        Console.WriteLine(url);

        string response = WebRequestPostData(url, jobInfo.ToString(), SfdcBinding.SessionHeaderValue.sessionId, "application/xml");

        var serializer = new XmlSerializer(typeof(JobInfo));

        JobInfo result;

        using (var stream = new StringReader(response))
        using (var reader = XmlReader.Create(stream))
        {
            result = (JobInfo)serializer.Deserialize(reader);
        }

        return result;
    }

    public static BatchInfoList GetBatches(string jobId)
    {

        string url = serverUrl + "/job/" + jobId + "/batch";

        Console.WriteLine(url);

        string response = WebRequestPostData(url, null, SfdcBinding.SessionHeaderValue.sessionId, "application/xml");

        var serializer = new XmlSerializer(typeof(BatchInfoList));

        BatchInfoList result;

        using (var stream = new StringReader(response))
        using (var reader = XmlReader.Create(stream))
        {
            result = (BatchInfoList)serializer.Deserialize(reader);
        }

        return result;
    }

    public static BatchInfo CreateBatch(IEnumerable<string> columns, IEnumerable<IEnumerable<string>> rows, string jobId)
    {
        // begin validation

        if (rows.Count() >= 10000)
        {
            throw new ArgumentException("The SalesForce Bulk API does not permit more than 10,000 rows per batch", "rows");
        }

        if (rows.First().Count() != columns.Count())
        {
            throw new InvalidOperationException("The number of columns in 'rows' does not match the number of 'columns'.");
        }

        if (operation == "update")
        {
            if (string.Join(",", columns).Contains("Id") == false)
            {
                throw new ArgumentException("You have specified an update operation but have not provided an 'Id' column.", "columns");
            }
        }

        // end validation

        string batchTest2 = CreateCsvFromIEnumerable(columns, rows);

        Console.Write(batchTest2);

        string batchUrl = serverUrl + "/job/" + jobId + "/batch";

        string batchResponse = WebRequestPostData(batchUrl, batchTest2, SfdcBinding.SessionHeaderValue.sessionId, "text/csv");

        var serializer = new XmlSerializer(typeof(BatchInfo));

        BatchInfo result;

        using (var stream = new StringReader(batchResponse))
        using (var reader = XmlReader.Create(stream))
        {
            result = (BatchInfo)serializer.Deserialize(reader);
        }

        return result;
    }

    private static string WebRequestPostData(string url, string postData, string sessionId, string contentType)
    {
        System.Net.WebRequest req = System.Net.WebRequest.Create(url);

        req.Headers.Add("X-SFDC-Session: " + sessionId);
        //req.Headers.Add("Content-Encoding: gzip");
        //req.ContentType = "application/xml";
        //req.ContentType = "text/csv";
        req.ContentType = contentType + "; UTF-8";
        req.Method = "GET";

        if (postData != null)
        {
            req.Method = "POST";

            byte[] bytes = System.Text.Encoding.ASCII.GetBytes(postData);
            req.ContentLength = bytes.Length;

            using (Stream os = req.GetRequestStream())
            {
                os.Write(bytes, 0, bytes.Length);
            }
        }

        using (System.Net.WebResponse resp = req.GetResponse())
        {
            if (resp == null) return null;

            using (System.IO.StreamReader sr = new System.IO.StreamReader(resp.GetResponseStream()))
            {
                return sr.ReadToEnd().Trim();
            }
        }
    }

    private static string CreateCsvFromIEnumerable(IEnumerable<string> columns, IEnumerable<IEnumerable<string>> rows)
    {
        string columnNames = "";
        int i = 0;

        foreach (string column in columns)
        {
            columnNames += column + ",";
            i++;
            if (i >= columns.Count())
                columnNames = columnNames.TrimEnd(',');
        }

        columnNames += "\r\n";

        string data = "";
        i = 0;

        foreach (IEnumerable<string> row in rows)
        {
            foreach (string _row in row)
            {
                data += "\"" + _row.Replace("\"", "\"\"") + "\",";
                i++;
                if (i >= row.Count())
                    data = data.TrimEnd(',');
            }

            i = 0;

            data += "\r\n";
        }

        return columnNames + data;
    }

}

[XmlRoot("batchInfoList", Namespace = "http://www.force.com/2009/06/asyncapi/dataload")]
public class BatchInfoList
{
    [XmlElement("batchInfo")]
    public List<BatchInfo> BatchInfo { get; set; }
}

[XmlRoot("jobInfo", Namespace = "http://www.force.com/2009/06/asyncapi/dataload")]
public class JobInfo
{
    [XmlElement("id")]
    public string Id { get; set; }

    [XmlElement("operation")]
    public string Operation { get; set; }

    [XmlElement("object")]
    public string Object { get; set; }

    [XmlElement("createdById")]
    public string CreatedById { get; set; }

    [XmlElement("createdDate")]
    public DateTime CreatedDate { get; set; }

    [XmlElement("systemModstamp")]
    public DateTime SystemModstamp { get; set; }

    [XmlElement("state")]
    public string State { get; set; }

    [XmlElement("concurrencyMode")]
    public string ConcurrencyMode { get; set; }

    [XmlElement("contentType")]
    public string ContentType { get; set; }

    [XmlElement("numberBatchesQueued")]
    public string NumberBatchesQueued { get; set; }

    [XmlElement("numberBatchesInProgress")]
    public string NumberBatchesInProgress { get; set; }

    [XmlElement("numberBatchesCompleted")]
    public string NumberBatchesCompleted { get; set; }

    [XmlElement("numberBatchesFailed")]
    public string NumberBatchesFailed { get; set; }

    [XmlElement("numberBatchesTotal")]
    public string NumberBatchesTotal { get; set; }

    [XmlElement("numberRecordsProcessed")]
    public string numberRecordsProcessed { get; set; }

    [XmlElement("numberRetries")]
    public string NumberRetries { get; set; }

    [XmlElement("apiVersion")]
    public string ApiVersion { get; set; }
}

[XmlRoot("batchInfo", Namespace = "http://www.force.com/2009/06/asyncapi/dataload")]
public class BatchInfo
{
    [XmlElement("id")]
    public string Id { get; set; }

    [XmlElement("jobId")]
    public string JobId { get; set; }

    [XmlElement("state")]
    public string State { get; set; }

    [XmlElement("createdDate")]
    public string CreatedDate { get; set; }

    [XmlElement("systemModstamp")]
    public string SystemModStamp { get; set; }

    [XmlElement("numberRecordsProcessed")]
    public string NumberRecordsProcessed { get; set; }

    [XmlElement("numberRecordsFailed")]
    public string NumberRecordsFailed { get; set; }

    [XmlElement("totalProcessingTime")]
    public string TotalProcessingTime { get; set; }

    [XmlElement("apiActiveProcessingTime")]
    public string ApiActiveProcessingTime { get; set; }

    [XmlElement("apexProcessingTime")]
    public string ApexProcessingTime { get; set; }
}

To use:

Modify these lines accordingly:

UserName = "YourUserName";
Password = "YourPassWord";

serverUrl = "https://naYOUR_SERVER_NUMBER.salesforce.com/services/async/28.0";

And, use like so:

IEnumerable<string> columns = new List<string>() 
{ 
    //"Id",
    "FirstName", 
    "LastName",
    "Department",
    "Birthdate",
    "Description",
    "CustomId__c"
};

IEnumerable<IEnumerable<string>> rows = new List<List<string>>()
{
    new List<string>()
    {
        //"",
        "Tom",
        "Jones",
        "Marketing",
        "1940-06-07Z",
        "Self-described as \"the top\" branding guru on the West Coast",
        "1000"
    },
    new List<string>()
    {
        //"",
        "Ian",
        "Dury",
        "R&D",
        "",
        "World-renowned expert in fuzzy logic design. Influential in technology purchases.",
        "1001"
    }
};

JobInfo job = SalesForceBulkApi.CreateJob(_operation: "upsert", objectType: "Contact", externalIdFieldName: "CustomId__c");

BatchInfo batch = SalesForceBulkApi.CreateBatch(columns: columns, rows: rows, jobId: job.Id);

//JobInfo response = SalesForceBulkApi.CloseJob("750i0000000QOSq");

//BatchInfoList batches = SalesForceBulkApi.GetBatches("750i0000000Q8vD");

Console.ReadLine();
2
  • What exactly was the problem? The code is helpful, but it would be better if you explained why it wasn't working and what you did to address that.
    – Mike Chale
    Commented Aug 22, 2013 at 18:04
  • @MikeChale I am not exactly sure what the problem was; I found out I was calling the wrong method at some point, so I changed it, and re-created my custom field in salesforce and changed some other things, and eventually it worked! Commented Aug 22, 2013 at 18:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .