diff --git a/.gitignore b/.gitignore
index a122c5f6..35e76fe9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,4 +120,10 @@ artifacts
# Test
testrunner
-scripts/output.txt
\ No newline at end of file
+scripts/output.txt
+/.vs
+/.vs
+/.vs/slnx.sqlite
+/src/.vs/config
+/src/.vs/config
+/src/.vs/config/applicationhost.config
diff --git a/samples/CompositeConsole/App.config b/samples/CompositeConsole/App.config
new file mode 100644
index 00000000..8d725cbd
--- /dev/null
+++ b/samples/CompositeConsole/App.config
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/samples/CompositeConsole/CompositeConsole.csproj b/samples/CompositeConsole/CompositeConsole.csproj
new file mode 100644
index 00000000..9e290683
--- /dev/null
+++ b/samples/CompositeConsole/CompositeConsole.csproj
@@ -0,0 +1,66 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {95CF1FA1-98E8-411C-89BF-DB7D086F27CE}
+ Exe
+ CompositeConsole
+ CompositeConsole
+ v4.6.1
+ 512
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Designer
+
+
+
+
+ {688BC3A2-29C2-48F7-BC28-7A81ABF5C3D3}
+ CommonLibrariesForNET
+
+
+ {1CC985BC-27F3-4F1D-8946-F4DDB084B58F}
+ ForceToolkitForNET
+
+
+
+
\ No newline at end of file
diff --git a/samples/CompositeConsole/CompositeConsole.sln b/samples/CompositeConsole/CompositeConsole.sln
new file mode 100644
index 00000000..8cf17fb8
--- /dev/null
+++ b/samples/CompositeConsole/CompositeConsole.sln
@@ -0,0 +1,34 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.26228.12
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CompositeConsole", "CompositeConsole.csproj", "{95CF1FA1-98E8-411C-89BF-DB7D086F27CE}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ForceToolkitForNET", "..\..\src\ForceToolkitForNET\ForceToolkitForNET.csproj", "{1CC985BC-27F3-4F1D-8946-F4DDB084B58F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CommonLibrariesForNET", "..\..\src\CommonLibrariesForNET\CommonLibrariesForNET.csproj", "{688BC3A2-29C2-48F7-BC28-7A81ABF5C3D3}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {95CF1FA1-98E8-411C-89BF-DB7D086F27CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {95CF1FA1-98E8-411C-89BF-DB7D086F27CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {95CF1FA1-98E8-411C-89BF-DB7D086F27CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {95CF1FA1-98E8-411C-89BF-DB7D086F27CE}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1CC985BC-27F3-4F1D-8946-F4DDB084B58F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1CC985BC-27F3-4F1D-8946-F4DDB084B58F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1CC985BC-27F3-4F1D-8946-F4DDB084B58F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1CC985BC-27F3-4F1D-8946-F4DDB084B58F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {688BC3A2-29C2-48F7-BC28-7A81ABF5C3D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {688BC3A2-29C2-48F7-BC28-7A81ABF5C3D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {688BC3A2-29C2-48F7-BC28-7A81ABF5C3D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {688BC3A2-29C2-48F7-BC28-7A81ABF5C3D3}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/samples/CompositeConsole/ExtensionMethods.cs b/samples/CompositeConsole/ExtensionMethods.cs
new file mode 100644
index 00000000..2e7df00b
--- /dev/null
+++ b/samples/CompositeConsole/ExtensionMethods.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CompositeConsole
+{
+ public static class ExtensionMethods
+ {
+ public static IEnumerable> Partition(this IList source, Int32 size)
+ {
+ for (int i = 0; i < (source.Count / size) + (source.Count % size > 0 ? 1 : 0); i++)
+ yield return new List(source.Skip(size * i).Take(size));
+ }
+ }
+}
diff --git a/samples/CompositeConsole/Program.cs b/samples/CompositeConsole/Program.cs
new file mode 100644
index 00000000..9f8e20aa
--- /dev/null
+++ b/samples/CompositeConsole/Program.cs
@@ -0,0 +1,160 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Salesforce.Common;
+using Salesforce.Force;
+
+namespace CompositeConsole
+{
+ class Program
+ {
+ private static readonly string SecurityToken = ConfigurationManager.AppSettings["SecurityToken"];
+ private static readonly string ConsumerKey = ConfigurationManager.AppSettings["ConsumerKey"];
+ private static readonly string ConsumerSecret = ConfigurationManager.AppSettings["ConsumerSecret"];
+ private static readonly string Username = ConfigurationManager.AppSettings["Username"];
+ private static readonly string Password = ConfigurationManager.AppSettings["Password"] + SecurityToken;
+ private static readonly string IsSandboxUser = ConfigurationManager.AppSettings["IsSandboxUser"];
+
+ public static void Main()
+ {
+ try
+ {
+ var task = RunSample();
+ task.Wait();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e.Message);
+ Console.WriteLine(e.StackTrace);
+
+ var innerException = e.InnerException;
+ while (innerException != null)
+ {
+ Console.WriteLine(innerException.Message);
+ Console.WriteLine(innerException.StackTrace);
+
+ innerException = innerException.InnerException;
+ }
+ }
+
+ Console.WriteLine("\nPress enter to close...");
+ Console.ReadLine();
+ }
+
+ private static async Task RunSample()
+ {
+ var auth = new AuthenticationClient();
+
+ // Authenticate with Salesforce
+ Console.WriteLine("Authenticating with Salesforce");
+ var url = IsSandboxUser.Equals("true", StringComparison.CurrentCultureIgnoreCase)
+ ? "https://test.salesforce.com/services/oauth2/token"
+ : "https://login.salesforce.com/services/oauth2/token";
+
+ await auth.UsernamePasswordAsync(ConsumerKey, ConsumerSecret, Username, Password, url);
+ Console.WriteLine("Connected to Salesforce");
+
+ // Get a bulk client
+ var client = new ForceClient(auth.InstanceUrl, auth.AccessToken, auth.ApiVersion);
+
+ //Create some Accounts using the SOobject Tree
+ var sObjectTreeRecords = new
+ {
+ records = new[]
+ {
+ new
+ {
+ attributes = new {type = "Account", referenceId = "ref1"},
+ name = "SampleAccount1",
+ phone = "1111111111",
+ website = "nicode.org",
+ numberOfEmployees = "1",
+ industry = "Software"
+ },
+ new
+ {
+ attributes = new {type = "Account", referenceId = "ref2"},
+ name = "SampleAccount2",
+ phone = "22222222222",
+ website = "nicode.org",
+ numberOfEmployees = "2",
+ industry = "Software"
+ },
+ new
+ {
+ attributes = new {type = "Account", referenceId = "ref3"},
+ name = "SampleAccount1",
+ phone = "3333333333",
+ website = "nicode.org",
+ numberOfEmployees = "3",
+ industry = "Software"
+ },
+ new
+ {
+ attributes = new {type = "Account", referenceId = "ref4"},
+ name = "SampleAccount4",
+ phone = "4444444444",
+ website = "nicode.org",
+ numberOfEmployees = "4",
+ industry = "Software"
+ },
+ }
+ };
+
+ var results = (await client.SObjectTreeSave("Account", sObjectTreeRecords)).results;
+
+ //Record the AccountIds to a List
+ var newAccountIds = new List();
+ foreach (var successResponseObjectTreeResult in results)
+ {
+ Console.WriteLine(successResponseObjectTreeResult.referenceId + " : " + successResponseObjectTreeResult.id);
+ newAccountIds.Add(successResponseObjectTreeResult.id);
+ }
+
+
+ //Using the partition extension method to split into partitions of 25 (requirement for batch request)
+ foreach (var newAccountIdPartition in newAccountIds.Partition(25))
+ {
+ //Update the industry of each Account
+ var updateObject = new
+ {
+ batchRequests = newAccountIdPartition
+ .Select(id => new
+ {
+ method = "PATCH",
+ url = "v34.0/sobjects/Account/" + id,
+ richInput = new { Industry = "Engineering" }
+ })
+ .ToArray()
+ };
+ var unused = (await client.BatchSave(updateObject)).results;
+ }
+
+ //Verifying industry changed
+ var accounts = (await client.QueryAsync("SELECT Id, Industry FROM Account WHERE website = 'nicode.org'")).Records;
+ foreach (var account in accounts)
+ {
+ Console.WriteLine(account.Id + " : " + account.Industry);
+ }
+
+ foreach (var accountPartition in accounts.Partition(25))
+ {
+ var deleteObjects = new
+ {
+ batchRequests = accountPartition
+ .Select(account => new { method = "DELETE", url = "v34.0/sobjects/Account/" + account.Id })
+ .ToArray()
+ };
+ var unused = (await client.BatchSave(deleteObjects)).results;
+ }
+ }
+ public class Account
+ {
+ public string Id { get; set; }
+ public string Industry { get; set; }
+ }
+ }
+}
diff --git a/samples/CompositeConsole/Properties/AssemblyInfo.cs b/samples/CompositeConsole/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..68f2c660
--- /dev/null
+++ b/samples/CompositeConsole/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CompositeConsole")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("CompositeConsole")]
+[assembly: AssemblyCopyright("Copyright © 2017")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("95cf1fa1-98e8-411c-89bf-db7d086f27ce")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/ChatterToolkitForNET/ChatterToolkitForNET.csproj b/src/ChatterToolkitForNET/ChatterToolkitForNET.csproj
index e64b890b..5957e902 100644
--- a/src/ChatterToolkitForNET/ChatterToolkitForNET.csproj
+++ b/src/ChatterToolkitForNET/ChatterToolkitForNET.csproj
@@ -20,6 +20,10 @@
2.0
+ SAK
+ SAK
+ SAK
+ SAK
true
diff --git a/src/CommonLibrariesForNET/CommonLibrariesForNET.csproj b/src/CommonLibrariesForNET/CommonLibrariesForNET.csproj
index ba151d83..a072c3db 100644
--- a/src/CommonLibrariesForNET/CommonLibrariesForNET.csproj
+++ b/src/CommonLibrariesForNET/CommonLibrariesForNET.csproj
@@ -20,6 +20,10 @@
2.0
+ SAK
+ SAK
+ SAK
+ SAK
true
@@ -63,6 +67,8 @@
+
+
@@ -82,6 +88,7 @@
+
diff --git a/src/CommonLibrariesForNET/Models/Json/SuccessResponseBatchSave.cs b/src/CommonLibrariesForNET/Models/Json/SuccessResponseBatchSave.cs
new file mode 100644
index 00000000..6905085c
--- /dev/null
+++ b/src/CommonLibrariesForNET/Models/Json/SuccessResponseBatchSave.cs
@@ -0,0 +1,34 @@
+using Newtonsoft.Json;
+
+namespace Salesforce.Common.Models.Json
+{
+
+ public class SuccessResponseBatchSave
+ {
+ public bool hasErrors { get; set; }
+ public dynamic results { get; set; }
+ }
+
+ public class SuccessResponseBatchSaveResult
+ {
+ public int statusCode { get; set; }
+ public SuccessResponseBatchSaveResult1 result { get; set; }
+ }
+
+ public class SuccessResponseBatchSaveResult1
+ {
+ public SuccessResponseBatchSaveResult1Attributes attributes { get; set; }
+ public string Name { get; set; }
+ public string BillingPostalCode { get; set; }
+ public string Id { get; set; }
+ }
+
+ public class SuccessResponseBatchSaveResult1Attributes
+ {
+ public string type { get; set; }
+ public string url { get; set; }
+ }
+
+
+}
+
diff --git a/src/CommonLibrariesForNET/Models/Json/SuccessResponseSObjectTree.cs b/src/CommonLibrariesForNET/Models/Json/SuccessResponseSObjectTree.cs
new file mode 100644
index 00000000..eee0e3e2
--- /dev/null
+++ b/src/CommonLibrariesForNET/Models/Json/SuccessResponseSObjectTree.cs
@@ -0,0 +1,17 @@
+using Newtonsoft.Json;
+
+namespace Salesforce.Common.Models.Json
+{
+ public class SuccessResponseSObjectTree
+ {
+ //[JsonProperty(PropertyName = "hasErrors")]
+ public bool hasErrors { get; set; }
+ public SuccessResponseObjectTreeResult[] results { get; set; }
+ }
+ public class SuccessResponseObjectTreeResult
+ {
+ public string referenceId { get; set; }
+ public string id { get; set; }
+ }
+}
+
diff --git a/src/CommonLibrariesForNET/Models/Xml/SObjectList.cs b/src/CommonLibrariesForNET/Models/Xml/SObjectList.cs
index f42ea89e..ffb9a4fe 100644
--- a/src/CommonLibrariesForNET/Models/Xml/SObjectList.cs
+++ b/src/CommonLibrariesForNET/Models/Xml/SObjectList.cs
@@ -4,6 +4,7 @@
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
+using Salesforce.Common.Serializer;
namespace Salesforce.Common.Models.Xml
{
@@ -32,10 +33,16 @@ public void WriteXml(XmlWriter writer)
}
else
{
- var xmlSerializer = new XmlSerializer(typeof(T), new XmlRootAttribute("sObject"));
+ XmlSerializer xmlSerializer;
+ if (!XmlSerializerCache.GetInstance().XmlSerializerDictionary.TryGetValue(typeof(T).FullName, out xmlSerializer))
+ {
+ xmlSerializer = new XmlSerializer(typeof(T), new XmlRootAttribute("sObject"));
+ XmlSerializerCache.GetInstance().XmlSerializerDictionary.Add(typeof(T).FullName, xmlSerializer);
+ }
+
var ns = new XmlSerializerNamespaces();
ns.Add(string.Empty, string.Empty);
- var settings = new XmlWriterSettings {OmitXmlDeclaration = true};
+ var settings = new XmlWriterSettings { OmitXmlDeclaration = true };
var stringBuilder = new StringBuilder();
using (var xmlWriter = XmlWriter.Create(stringBuilder, settings))
{
diff --git a/src/CommonLibrariesForNET/XmlSerializerCache.cs b/src/CommonLibrariesForNET/XmlSerializerCache.cs
new file mode 100644
index 00000000..93fb93af
--- /dev/null
+++ b/src/CommonLibrariesForNET/XmlSerializerCache.cs
@@ -0,0 +1,25 @@
+using System.Collections.Generic;
+using System.Xml.Serialization;
+
+namespace Salesforce.Common
+{
+
+ public class XmlSerializerCache
+ {
+ private static XmlSerializerCache _instance;
+
+ private XmlSerializerCache()
+ {
+ XmlSerializerDictionary = new Dictionary();
+ }
+
+ public static XmlSerializerCache GetInstance()
+ {
+ if (_instance == null)
+ _instance = new XmlSerializerCache();
+ return _instance;
+ }
+
+ public Dictionary XmlSerializerDictionary { get; set; }
+ }
+}
diff --git a/src/ForceToolkitForNET/ForceClient.cs b/src/ForceToolkitForNET/ForceClient.cs
index dd7149b1..93047523 100644
--- a/src/ForceToolkitForNET/ForceClient.cs
+++ b/src/ForceToolkitForNET/ForceClient.cs
@@ -206,6 +206,20 @@ public async Task UserInfo(string url)
return response;
}
+ public async Task SObjectTreeSave(string objectName, object sObjectTree)
+ {
+ if (string.IsNullOrEmpty(objectName)) throw new ArgumentNullException("objectName");
+ if (sObjectTree == null) throw new ArgumentNullException("sObjectTree");
+
+ return await _jsonHttpClient.HttpPostAsync(sObjectTree, string.Format("composite/tree/{0}/", objectName));
+ }
+ public async Task BatchSave(object batchRequest)
+ {
+ if (batchRequest == null) throw new ArgumentNullException("batchRequest");
+
+ return await _jsonHttpClient.HttpPostAsync(batchRequest, "composite/batch/");
+ }
+
// BULK METHODS
public async Task> RunJobAsync(string objectName, BulkConstants.OperationType operationType,
diff --git a/src/ForceToolkitForNET/ForceToolkitForNET.csproj b/src/ForceToolkitForNET/ForceToolkitForNET.csproj
index 9592b245..e5bb9b97 100644
--- a/src/ForceToolkitForNET/ForceToolkitForNET.csproj
+++ b/src/ForceToolkitForNET/ForceToolkitForNET.csproj
@@ -20,6 +20,10 @@
2.0
+ SAK
+ SAK
+ SAK
+ SAK
true
@@ -61,7 +65,7 @@
- {688bc3a2-29c2-48f7-bc28-7a81abf5c3d3}
+ {688BC3A2-29C2-48F7-BC28-7A81ABF5C3D3}
CommonLibrariesForNET
@@ -73,4 +77,4 @@
-->
-
+
\ No newline at end of file