CSOM Performance Optimization: Why You Should Batch Your SharePoint Operations

Aug 31, 2025 min read

The Problem: One-by-One Operations Kill Performance

Following up on my previous post about DevProxy and throttling testing, there’s another critical performance issue I see regularly in SharePoint CSOM code: executing operations one by one instead of batching them.

Consider this common pattern that I see everywhere:

// ❌ This approach is slow and inefficient
foreach (var id in itemIds)
{
    var item = list.GetItemById(id);
    context.Load(item);
    await context.ExecuteQueryAsync(); // Executing for EACH item!
    
    // Process the item
    ProcessItem(item);
}

This code makes a server round-trip for every single item. If you’re processing 50 items, that’s 50 separate calls to SharePoint. Each call has network latency and server processing time.

TL;DR

Problem: Executing CSOM operations one-by-one creates a server call for each item (50 items = 50 server calls = ~3-5 seconds).

Solution: Batch up to 100 operations before calling ExecuteQueryAsync() (50 items = 1 server call = ~200-500ms).

Result: 10x+ performance improvement with minimal code changes. Use the helper methods below to batch any CSOM operations.

The Solution: Batch Operations

SharePoint CSOM reliably handles batches of around 100 operations before calling ExecuteQueryAsync(). This means you can dramatically reduce the number of server round-trips.

Microsoft’s official documentation also emphasizes this pattern in their performance guidelines, showing how grouping data retrieval operations significantly improves performance.

Here’s the pattern I use in most of my SharePoint projects:

The Helper Methods

public static async Task<List<T>> GetItemsByIds<T>(this List list, IEnumerable<int> ids) 
    where T : new()
{
    if (ids == null || !ids.Any())
    {
        return new List<T>();
    }

    var listItems = await CSOMHelpers.ProcessInChunks(ids.ToList(), 100, async chunk => {
        var items = chunk.Select(id => {
            var item = list.GetItemById(id);
            list.Context.Load(item);
            return item;
        }).ToList();

        await ExecuteQuery(list.Context, () => { /* Load calls already done above */ });
        
        return items;
    });

    return listItems.ToList();
}

internal static async Task<List<TOut>> ProcessInChunks<TIn, TOut>(List<TIn> source, int chunkSize, Func<List<TIn>, Task<List<TOut>>> action)
{
    var result = new List<TOut>();
    foreach (var chunk in source.Chunk(chunkSize))
    {
        result.AddRange(await action(chunk.ToList()));
    }
    return result;
}

Performance Impact: The Numbers

Let me show you the difference with a real-world example. Loading 50 SharePoint list items:

Before (One-by-One)

  • 50 server calls
  • Network latency: 50ms × 50 = 2.5 seconds
  • Actual total time is usually higher due to server-side processing, hence: ~3-5 seconds

After (Batched)

  • 1 server call (all 50 items in one batch)
  • Network latency: 50ms × 1 = 50ms
  • Total time: ~200-500ms

⚠️ Performance Disclaimer

The numbers shown above are from a specific example scenario and are meant to illustrate the concept of batching benefits. Your actual performance gains will vary significantly based on:

  • Network latency and bandwidth
  • SharePoint server load and location
  • Item complexity and field count
  • Query complexity and filtering
  • Authentication overhead
  • Time of day and concurrent users

While batching almost always improves performance, the exact improvement factor can range from marginal gains to dramatic improvements. The key takeaway is the pattern and approach, not the specific numbers.

Applying the Pattern to CRUD Operations

This pattern works for all CSOM operations, not just reading, but for all the CRUD operations.

Why 100 Items?

SharePoint has practical limits on how many operations you can batch:

  • CSOM limit: Around 100 operations per batch
  • REST API limit: Different limits depending on operation type
  • Network payload: Larger batches mean bigger HTTP requests

I’ve found 100 items to be the sweet spot that:

  • Maximizes performance gains
  • Stays well within SharePoint limits
  • Keeps HTTP payloads manageable
  • Works reliably across different SharePoint environments

Combining with Throttling Protection

When you combine this batching approach with the throttling protection patterns from my DevProxy post, you get robust, high-performance SharePoint applications.

Best Practices

  1. Batch whenever you can - even for small numbers of items, batching usually helps.
  2. Use 100 as your chunk size for most scenarios
  3. Combine with retry logic for production resilience
  4. Test with DevProxy to ensure your batching works under throttling conditions
  5. Monitor performance before and after implementing batching

The Bottom Line

Batching CSOM operations is one of the easiest wins for SharePoint performance optimization. The code pattern is straightforward to implement and reuse, but the performance impact is dramatic.

Stop executing SharePoint operations one by one. Your users (and SharePoint servers) will thank you.

Resources

Jeppe Spanggaard

A passionate software developer. I love to build software that makes a difference!