Last active
March 19, 2021 04:18
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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"); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ... | |
| 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; | |
| } | |
| ... |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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"); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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