Skip to content

Instantly share code, notes, and snippets.

@wadewegner
Last active March 19, 2021 04:18
Show Gist options
  • Select an option

  • Save wadewegner/9140481 to your computer and use it in GitHub Desktop.

Select an option

Save wadewegner/9140481 to your computer and use it in GitHub Desktop.
.NET code used for interacting with the SOAP and Metadata APIs without using proxy classes generated by WSDLs
private static async Task<string> Create(string query, string sessionId, string metadataServerUrl)
{
var wsdlNamespace = "http://soap.sforce.com/2006/04/metadata";
var header = "";
var action = "create";
var soap = string.Format(
@"<soapenv:Envelope xmlns:xsd=""http://www.w3.org/2001/XMLSchema""
xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/""
xmlns:cmd=""{0}""
xmlns:apex=""http://soap.sforce.com/2006/08/apex"">
<soapenv:Header>
<cmd:SessionHeader>
<cmd:sessionId>{1}</cmd:sessionId>
</cmd:SessionHeader>
{2}
</soapenv:Header>
<soapenv:Body>
<{3} xmlns=""{4}"">
{5}
</{6}>
</soapenv:Body>
</soapenv:Envelope>", wsdlNamespace, sessionId, header, action, wsdlNamespace, query, action);
var content = new StringContent(soap, Encoding.UTF8, "text/xml");
using (var httpClient = new HttpClient())
{
var request = new HttpRequestMessage();
request.RequestUri = new Uri(metadataServerUrl);
request.Method = HttpMethod.Post;
request.Content = content;
request.Headers.Add("SOAPAction", action);
var responseMessage = await httpClient.SendAsync(request);
var response = await responseMessage.Content.ReadAsStringAsync();
if (responseMessage.IsSuccessStatusCode)
{
return response;
}
}
throw new Exception("Failed create object");
}
private static async Task<CreateResult> CreateCustomField(string customObject, string fieldName, string sessionId, string metadataServerUrl)
{
var customFieldQuery = string.Format(
@"<metadata xsi:type=""CustomField"" xmlns:cmd=""http://soap.sforce.com/2006/04/metadata"">
<fullName>{0}.{1}__c</fullName>
<label>{1}</label>
<length>100</length>
<type>Text</type>
</metadata>", customObject + "__c", fieldName); // TODO: pass this in for flexibility
var customFieldResponse = await Create(customFieldQuery, sessionId, metadataServerUrl);
var resultXml = new XmlDocument();
var mgr = new XmlNamespaceManager(resultXml.NameTable);
mgr.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
mgr.AddNamespace("meta", "http://soap.sforce.com/2006/04/metadata");
resultXml.LoadXml(customFieldResponse);
var selectSingleNode = resultXml.SelectSingleNode("//meta:createResponse", mgr);
if (selectSingleNode == null) return null;
var loginResultNode = selectSingleNode.InnerXml;
var serializer = new XmlSerializer(typeof(CreateResult));
var stringReader = new StringReader(loginResultNode);
var createResult = (CreateResult)serializer.Deserialize(stringReader);
stringReader.Dispose();
return createResult;
}
private static async Task<CreateResult> CreateCustomObject(string customObject, string sessionId, string metadataServerUrl)
{
var customObjectQuery = string.Format(
@"<metadata xsi:type=""CustomObject"" xmlns:cmd=""http://soap.sforce.com/2006/04/metadata"">
<fullName>{0}__c</fullName>
<label>{0}</label>
<pluralLabel>{0}</pluralLabel>
<deploymentStatus>Deployed</deploymentStatus>
<sharingModel>ReadWrite</sharingModel>
<nameField>
<label>ID</label>
<type>AutoNumber</type>
</nameField>
</metadata>", customObject);
var customObjectResponse = await Create(customObjectQuery, sessionId, metadataServerUrl);
var resultXml = new XmlDocument();
var mgr = new XmlNamespaceManager(resultXml.NameTable);
mgr.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
mgr.AddNamespace("meta", "http://soap.sforce.com/2006/04/metadata");
resultXml.LoadXml(customObjectResponse);
var selectSingleNode = resultXml.SelectSingleNode("//meta:createResponse", mgr);
if (selectSingleNode == null) return null;
var loginResultNode = selectSingleNode.InnerXml;
var serializer = new XmlSerializer(typeof (CreateResult));
var stringReader = new StringReader(loginResultNode);
var createResult = (CreateResult) serializer.Deserialize(stringReader);
stringReader.Dispose();
return createResult;
}
...
if (responseMessage.IsSuccessStatusCode)
{
var resultXml = new XmlDocument();
var mgr = new XmlNamespaceManager(resultXml.NameTable);
mgr.AddNamespace("soapenv", "http://schemas.xmlsoap.org/soap/envelope/");
mgr.AddNamespace("ns", string.Format("urn:{0}.soap.sforce.com", wsdlType));
resultXml.LoadXml(response);
var loginResultNode = resultXml.SelectSingleNode("//ns:loginResponse", mgr).InnerXml;
var serializer = new XmlSerializer(typeof(T));
var stringReader = new StringReader(loginResultNode);
var loginResult = (T)serializer.Deserialize(stringReader);
stringReader.Dispose();
return loginResult;
}
...
using System.Xml.Serialization;
namespace Tester.Models.Enterprise
{
[XmlRoot(Namespace = "urn:enterprise.soap.sforce.com", ElementName = "result", IsNullable = true)]
public class LoginResult
{
[XmlElement(ElementName = "metadataServerUrl")]
public string MetadataServerUrl;
[XmlElement(ElementName = "passwordExpired")]
public bool PasswordExpired;
[XmlElement(ElementName = "sandbox")]
public bool Sandbox;
[XmlElement(ElementName = "serverUrl")]
public string ServerUrl;
[XmlElement(ElementName = "sessionId")]
public string SessionId;
[XmlElement(ElementName = "userId")]
public string UserId;
[XmlElement(ElementName = "userInfo")]
public UserInfoResult UserInfo;
}
}
namespace Tester.Models.Partner
{
[XmlRoot(Namespace = "urn:partner.soap.sforce.com", ElementName = "result", IsNullable = true)]
public class LoginResult
{
[XmlElement(ElementName = "metadataServerUrl")]
public string MetadataServerUrl;
[XmlElement(ElementName = "passwordExpired")]
public bool PasswordExpired;
[XmlElement(ElementName = "sandbox")]
public bool Sandbox;
[XmlElement(ElementName = "serverUrl")]
public string ServerUrl;
[XmlElement(ElementName = "sessionId")]
public string SessionId;
[XmlElement(ElementName = "userId")]
public string UserId;
[XmlElement(ElementName = "userInfo")]
public UserInfoResult UserInfo;
}
}
private static void Main()
{
AsyncMain().Wait();
}
static async Task AsyncMain()
{
const string userName = "[email protected]";
const string password = "password";
const string orgId = "0DFi00000008UYO";
const string customObject = "MyCustomObject";
const string fieldName = "MyCustomField";
var loginResultEnterprise = await Login<Enterprise.LoginResult>(userName, password, orgId);
Console.WriteLine(loginResultEnterprise.UserId);
var loginResultPartner = await Login<Partner.LoginResult>(userName, password, orgId);
Console.WriteLine(loginResultPartner.UserId);
var createObjectResult = await CreateCustomObject(customObject, loginResultPartner.SessionId, loginResultPartner.MetadataServerUrl);
Console.WriteLine(createObjectResult.Id);
var createFieldResult = await CreateCustomField(customObject, fieldName, loginResultPartner.SessionId, loginResultPartner.MetadataServerUrl);
Console.WriteLine(createFieldResult.Id);
}
static async Task<T> Login<T>(string userName, string password, string orgId)
{
string url;
string soap;
string wsdlType;
if (typeof(T) == typeof(Enterprise.LoginResult))
{
url = "https://login.salesforce.com/services/Soap/c/29.0/" + orgId;
soap = string.Format(
@"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"">
<soapenv:Body>
<login xmlns=""urn:enterprise.soap.sforce.com"">
<username>{0}</username>
<password>{1}</password>
</login>
</soapenv:Body>
</soapenv:Envelope>", userName, password);
wsdlType = "enterprise";
}
else
{
url = "https://login.salesforce.com/services/Soap/u/29.0/" + orgId;
soap = string.Format(@"
<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/"">
<soapenv:Body>
<login xmlns=""urn:partner.soap.sforce.com"">
<username>{0}</username>
<password>{1}</password>
</login>
</soapenv:Body>
</soapenv:Envelope>", userName, password);
wsdlType = "partner";
}
var content = new StringContent(soap, Encoding.UTF8, "text/xml");
using (var httpClient = new HttpClient())
{
var request = new HttpRequestMessage();
request.RequestUri = new Uri(url);
request.Method = HttpMethod.Post;
request.Content = content;
request.Headers.Add("SOAPAction", "login");
var responseMessage = await httpClient.SendAsync(request);
var response = await responseMessage.Content.ReadAsStringAsync();
if (responseMessage.IsSuccessStatusCode)
{
...
}
throw new Exception("Failed login");
}
}
using System.Xml.Serialization;
namespace Tester.Models.Enterprise
{
[XmlRoot(Namespace = "urn:enterprise.soap.sforce.com", ElementName = "userInfo", IsNullable = true)]
public class UserInfoResult
{
[XmlElement(ElementName = "accessibilityMode")]
public bool AccessibilityMode;
[XmlElement(ElementName = "currencySymbol")]
public string CurrencySymbol;
[XmlElement(ElementName = "orgAttachmentFileSizeLimit")]
public int OrgAttachmentFileSizeLimit;
[XmlElement(ElementName = "orgDefaultCurrencyIsoCode")]
public string OrgDefaultCurrencyIsoCode;
[XmlElement(ElementName = "orgDisallowHtmlAttachments")]
public bool OrgDisallowHtmlAttachments;
[XmlElement(ElementName = "orgHasPersonAccounts")]
public bool OrgHasPersonAccounts;
[XmlElement(ElementName = "organizationId")]
public string OrganizationId;
[XmlElement(ElementName = "organizationMultiCurrency")]
public bool OrganizationMultiCurrency;
[XmlElement(ElementName = "organizationName")]
public string OrganizationName;
[XmlElement(ElementName = "profileId")]
public string ProfileId;
[XmlElement(ElementName = "roleId")]
public string RoleId;
[XmlElement(ElementName = "sessionSecondsValid")]
public int SessionSecondsValid;
[XmlElement(ElementName = "userDefaultCurrencyIsoCode")]
public string UserDefaultCurrencyIsoCode;
[XmlElement(ElementName = "userEmail")]
public string UserEmail;
[XmlElement(ElementName = "userFullName")]
public string UserFullName;
[XmlElement(ElementName = "userId")]
public string UserId;
[XmlElement(ElementName = "userLanguage")]
public string UserLanguage;
[XmlElement(ElementName = "userLocale")]
public string UserLocale;
[XmlElement(ElementName = "userName")]
public string UserName;
[XmlElement(ElementName = "userTimeZone")]
public string UserTimeZone;
[XmlElement(ElementName = "userType")]
public string UserType;
[XmlElement(ElementName = "userUiSkin")]
public string UserUiSkin;
}
}
namespace Tester.Models.Partner
{
[XmlRoot(Namespace = "urn:partner.soap.sforce.com", ElementName = "userInfo", IsNullable = true)]
public class UserInfoResult
{
[XmlElement(ElementName = "accessibilityMode")]
public bool AccessibilityMode;
[XmlElement(ElementName = "currencySymbol")]
public string CurrencySymbol;
[XmlElement(ElementName = "orgAttachmentFileSizeLimit")]
public int OrgAttachmentFileSizeLimit;
[XmlElement(ElementName = "orgDefaultCurrencyIsoCode")]
public string OrgDefaultCurrencyIsoCode;
[XmlElement(ElementName = "orgDisallowHtmlAttachments")]
public bool OrgDisallowHtmlAttachments;
[XmlElement(ElementName = "orgHasPersonAccounts")]
public bool OrgHasPersonAccounts;
[XmlElement(ElementName = "organizationId")]
public string OrganizationId;
[XmlElement(ElementName = "organizationMultiCurrency")]
public bool OrganizationMultiCurrency;
[XmlElement(ElementName = "organizationName")]
public string OrganizationName;
[XmlElement(ElementName = "profileId")]
public string ProfileId;
[XmlElement(ElementName = "roleId")]
public string RoleId;
[XmlElement(ElementName = "sessionSecondsValid")]
public int SessionSecondsValid;
[XmlElement(ElementName = "userDefaultCurrencyIsoCode")]
public string UserDefaultCurrencyIsoCode;
[XmlElement(ElementName = "userEmail")]
public string UserEmail;
[XmlElement(ElementName = "userFullName")]
public string UserFullName;
[XmlElement(ElementName = "userId")]
public string UserId;
[XmlElement(ElementName = "userLanguage")]
public string UserLanguage;
[XmlElement(ElementName = "userLocale")]
public string UserLocale;
[XmlElement(ElementName = "userName")]
public string UserName;
[XmlElement(ElementName = "userTimeZone")]
public string UserTimeZone;
[XmlElement(ElementName = "userType")]
public string UserType;
[XmlElement(ElementName = "userUiSkin")]
public string UserUiSkin;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment