Whilst writing a process to upload instore purchases to Facebook using their offline conversion API I discovered that their API only accepts a maximum of 2,100 events in any one API call. As I already ahd a nice list of events I had a look around and managed to find this extension method for lists to break them into chunks of a specified size.

public static class ListExtensions
{
    public static List<List<T>> ChunkBy<T>(this List<T> source, int chunkSize)
    {
        return source
            .Select((x, i) => new { Index = i, Value = x })
            .GroupBy(x => x.Index / chunkSize)
            .Select(x => x.Select(v => v.Value).ToList())
            .ToList();
    }
}

I can then call loop through the resulting list of lists like so.

// Get transactions
List<Data> data = Common.GetData();

// Split into chunks of 2100 as this is maximum FB can process
List<List<Data>> dataChunks = data.ChunkBy(2100);

using (HttpClient client = new HttpClient())
{
	foreach (List<Data> dataChunk in dataChunks)
	{
		string dataString = JsonConvert.SerializeObject(dataChunk, Formatting.None, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });

		// Upload to Facebook
		string url = "https://graph.facebook.com/v2.10/" + ConfigurationManager.AppSettings["OfflineConversionDatasetId"] + "/events";

		List<KeyValuePair<string, string>> formFields = new List<KeyValuePair<string, string>>();

		string formString = "access_token=" + ConfigurationManager.AppSettings["AccessToken"];
		formString += "&upload_tag=" + ConfigurationManager.AppSettings["UploadTag"];
		formString += "&data=" + dataString;

		StringContent stringContent = new StringContent(formString, Encoding.UTF8, "application/json");

		HttpResponseMessage response = client.PostAsync(url, stringContent).Result;

		response.EnsureSuccessStatusCode();
	}
}

2 Comments

Tim · 14th May 2020 at 5:10 pm

Thanks for posting the cool code. You don’t want to create a new HttpClient for every loop. I’m surprised you haven’t suffered from socket-exhaustion yet

    Shinigami · 15th May 2020 at 9:43 am

    Thanks for pointing that out. I never hit any issues with the code but I agree that it’s the wrong way of doing it and that HttpClient should be reused. I’ve updated the example accordingly to avoid leading anyone astray.

Leave a Reply

Your email address will not be published. Required fields are marked *