1
votes

I need to programmatically upload PDF files to existing Salesforce records using the C# SOAP API.

I've seen this question as well as countless others but everything I've found either hasn't solved my issue or is in relation to another language or the REST API. All of the C# SOAP API information I'm able to find uses the Attachment object, which seems to not exist in the API. I'm using the Partner WSDL and correctly added it as a Web Reference to my project.

There does exist an EmailFileAttachment object but it's unable to be cast into an sObject to be saved to the Salesforce record.

This is the code I currently have and I'm able to update data fields just fine.

// Login
var Binding = new SforceService();
var loginResult = Binding.login("Username", "Password");
Binding.Url = loginResult.serverUrl;
Binding.SessionHeaderValue = new SessionHeader();
Binding.SessionHeaderValue.sessionId = loginResult.sessionId;

// Create sObject, update data
var obj = new sObject();
obj.type = "tableName";
obj.Id = "objId";

var document = new XmlDocument();
var nodeList = new List<XmlElement>();

var dataNode = document.CreateElement("fieldName");
dataNode.InnerText = "fieldValue";
nodeList.Add(dataNode);

obj.Any = nodeList.ToArray();

// Save data
sObject[] objArray = { obj };
var saveResults = Binding.update(objArray);

However I'm unable to use Attachment or EmailFileAttachment. Attachment doesn't exist in the Salesforce API namespace that I know of. EmailFileAttachment has all the correct fields that I need, but can't be converted to an sObject, so I'm guessing it's not the correct object to be using or that I'm going about using it incorrectly.

// The type or namespace 'Attachment' could not be found.
//var attachment = new Attachment;

var attachment = new EmailFileAttachment();
attachment.id = "objId";
attachment.fileName = "filename.pdf";
attachment.body = fileData; // fileData is byte[]

// Cannot implicitely convert EmailFileAttachment to sObject.
var objArray = new sObject[] { attachment };

Am I missing something to be able to use Attachment, or is there another way to do this?

Edit: I'm using Salesforce C# SOAP API v45.0


EDIT: As per superfell's answer I uploaded the file as an sObject which worked perfectly. The only stipulation is that the Attachment has to be created as a new Salesforce object, and linked to the original object via the ParentId field. Here is the working code below

var atObj = new sObject();

// Set type as "Attachment"
atObj.type = "Attachment";

var document = new XmlDocument();
var nodeList = new List<XmlElement>();

// Set Filename
var nameNode = document.CreateElement("Name");
nameNode.InnerText = "testfile.pdf";
nodeList.Add(nameNode);

// Set ParentId - this is the object id you want to link the attachment to
var idNode = document.CreateElement("ParentId");
idNode.InnerText = "objId";
nodeList.Add(idNode);

// Set File Data - use Base64 string conversion
string encoded = Convert.ToBase64String(fileData); // fileData is byte[]
var dataNode = document.CreateElement("Body");
dataNode.InnerText = encoded;
nodeList.Add(dataNode);

atObj.Any = nodeList.ToArray();
sObject[] objArray = { atObj };

// Use create(), not update()
var saveResults = Binding.create(objArray);
2

2 Answers

1
votes

The partner API includes only a generic SObject definition, none of the concrete SObject types. (because its expected that partners will want to write the code once and have it dynamically adapt to the users schema). You'll either need to build a partner SObject instance for your attachment, or switch to the enterprise WSDL which will have the concrete Attachment class you're looking for. See [1] for details of how SObjects look in the partner API. There's an example in C# for the partner API to create an SObject instance at [2] You should be able to change this to create an attachment instead of a contact.

[1] https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sforce_api_partner_objects.htm

[2] https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/sample_create_call.htm#topic-title

0
votes

Using Enterprise WSDL / SOAP to Attach a File / ContentDocument to an object I needed to Create a ContentVersion, which then Salesforce creates the ContentDocument then create a ContentDocumentLink to attach it to a object.

 Byte[] bytes = File.ReadAllBytes(filePath);
            
 ContentVersion cv = new ContentVersion();
 cv.Title = Path.GetFileName(filePath);
 cv.PathOnClient = filePath;
 cv.VersionData = bytes;
 cv.Origin = "H";

SaveResult[] createResults = _sfClient.create(new sObject[] { cv });

 if (createResults.Count() > 0 && createResults[0].success)
                {
                    cv.Id = createResults[0].id;

                    QueryResult queryResult = _sfClient.query(string.Format("select Id, ContentDocumentId from ContentVersion where Id = '{0}'", cv.Id));

                    if (queryResult.records == null) return null;

                    sObject[] records = queryResult.records;

                    //we can loop through the returned records
                    if (records.Length > 0)
                    {
                        List<ContentVersion> cvs = new List<ContentVersion>(queryResult.records.Cast<ContentVersion>());
                        ContentDocumentLink contentLink = new ContentDocumentLink();
                        contentLink.LinkedEntityId = sfCase.Id;
                        contentLink.ContentDocumentId = cvs.First().ContentDocumentId;
                        contentLink.ShareType = "V"; 
                        contentLink.IsDeleted = false;
                

                        SaveResult[] createResults1 = _sfClient.create(new sObject[] { contentLink });

                        if (createResults1.Count() > 0 && createResults1[0].success)
                        {

                            return createResults1[0].id;
                        }
                        else if (createResults1.Count() > 0)
                        {
                            return "Error. Case Content Document Failed. Case Number: [" + Helpers.Trim(sfCase.Case_Number__c) + "] had an error inserting. The error reported was: " + createResults1[0].errors[0].message;
                        }
                    }

                }
                else if (createResults.Count() > 0)
                {
                    return "Error. Case Content Version Failed. Case Number: [" + Helpers.Trim(sfCase.Case_Number__c) + "] had an error inserting. The error reported was: " + createResults[0].errors[0].message;
                }
            }
            catch (Exception ex)
            {
                return string.Format("Upsert {0}", ex.Message);
        }