# Jeppe Spanggaard - Software Developer > Blog about building software with C#/.NET, Azure, Microsoft Graph and SharePoint - real problems from real projects. Jeppe Spanggaard is a Danish software developer and co-founder of Pinksky, working with C#/.NET, Azure, Microsoft Graph, SharePoint and Microsoft 365. Every post here comes from a problem hit on a real project, with the numbers and the mistakes included. Site: https://jeppe-spanggaard.dk/ About the author: https://jeppe-spanggaard.dk/about/ RSS feed: https://jeppe-spanggaard.dk/blogs/index.xml GitHub: https://github.com/jeppesc11 Open-source contributions: - pnp/pnpcore#1802 (open): #1799 - CAML queries with Joins/ProjectedFields do not return projected fields - add CSOM-based GetItems support. Closes #1799 - microsoftgraph/msgraph-sdk-dotnet-core#980 (open): feat: Added statusCodesToTreatAsSuccess (additive success set) to NewBatchWithFailedRequests and Preserving RequestId - pnp/pnpframework#1202 (open): feat: Add GetDifferences method for ListItem field change detection - pnp/pnpframework#1195 (closed): Added EnsureAndSetUserFields method to ClientContextExtensions Community calls (Microsoft 365 & Power Platform): - Jan 2026, community demos call: "CSOM Performance: Fast Taxonomy Loading for SharePoint List Items with Many Terms" - https://pnp.github.io/blog/weekly-agenda/26-01-26/ - Sep 2023, weekly call: "Guest User Overview", built for HackTogether: Microsoft Teams Global Hack, Productivity category winner - https://pnp.github.io/blog/weekly-agenda/23-09-04/ Every blog post is also available as raw Markdown: append index.md to the post URL. ## Topics ### SharePoint Development (24 posts) Real SharePoint development problems in C#: provisioning, list items, navigation, content types and the API costs nobody warns you about. Topic page: https://jeppe-spanggaard.dk/tags/sharepoint/ Broad topic: these posts are listed under the narrower topics below. ### C# (22 posts) Everything on this blog written in C#: Microsoft Graph and CSOM client code, Azure Functions, reflection tricks, and the .NET side of Microsoft 365. Topic page: https://jeppe-spanggaard.dk/tags/csharp/ Broad topic: these posts are listed under the narrower topics below. ### SharePoint CSOM Performance (7 posts) Batching, CAML joins, change detection and ExceptionHandlingScope: how to stop SharePoint CSOM making tens of thousands of API calls it never needed to make. Topic page: https://jeppe-spanggaard.dk/tags/csom/ Start here: The SharePoint CSOM Performance Playbook: Stop Paying for Wasted API Calls - https://jeppe-spanggaard.dk/blogs/sharepoint-csom-performance-playbook/ - [CSOM vs SharePoint REST vs Graph: My Pick-One Playbook](https://jeppe-spanggaard.dk/blogs/csom-vs-sharepoint-rest-vs-graph/) (2026-07-30): Learn when to use CSOM, SharePoint REST or Microsoft Graph for SharePoint work, and which operations each one silently refuses to do. - [The SharePoint CSOM Performance Playbook: Stop Paying for Wasted API Calls](https://jeppe-spanggaard.dk/blogs/sharepoint-csom-performance-playbook/) (2026-05-20): Learn how to speed up SharePoint CSOM with five proven techniques - batching, CAML joins, change detection, server-side exception handling, and fast taxonomy loading. - [CSOM Performance: Fast Taxonomy Loading for SharePoint List Items with Many Terms](https://jeppe-spanggaard.dk/blogs/csom-taxonomy-performance-optimization/) (2025-12-21): An undocumented but effective approach to dramatically speed up CSOM taxonomy field loading when dealing with SharePoint list items that have many terms. - [SharePoint's Server-Side Try-Catch: ExceptionHandlingScope](https://jeppe-spanggaard.dk/blogs/sharepoint-exceptionhandlingscope-csom-ensureuser/) (2025-10-20): Discover how SharePoint's ExceptionHandlingScope can optimize your API calls, reduce costs, and enhance performance with server-side exception handling. - [CSOM Performance Optimization: Why You Should Batch Your SharePoint Operations](https://jeppe-spanggaard.dk/blogs/csom-performance-optimization-chunking/) (2025-08-31): Learn how to dramatically improve CSOM performance by batching operations instead of executing them one by one - [Efficient Multi-List Queries in CSOM: Using CAML Joins with CAMLEX](https://jeppe-spanggaard.dk/blogs/joining-multiple-lists-csom-caml/) (2025-07-28): Learn how to efficiently query multiple SharePoint lists using CAML joins instead of multiple API calls to avoid throttling ### Microsoft Graph in C# (11 posts) Microsoft Graph from .NET: batching and mapping responses back to requests, rich notifications, throttling, and the SDK behaviour the docs leave out. Topic page: https://jeppe-spanggaard.dk/tags/microsoft-graph/ - [SharePoint News Links via Graph SDK: Filling in the Gaps the Docs Left Behind](https://jeppe-spanggaard.dk/blogs/graph-beta-sdk-news-link-with-banner-image/) (2026-03-03): Learn how to create SharePoint News Link pages with a banner image using the Microsoft Graph Beta SDK in C# — including the multipart upload the official documentation leaves completely undocumented. - [Graph Batching for File Content: Mapping Requests to Responses](https://jeppe-spanggaard.dk/blogs/graph-batching-file-content-mapping/) (2025-09-10): How to handle Graph batching when downloading file content and mapping responses back to original requests - [Export files as zip from SharePoint](https://jeppe-spanggaard.dk/blogs/download-multiple-files-from-sharepoint/) (2024-12-01): Download files from SharePoint as a zip, as is possible through the UI ### Microsoft 365 API Performance (12 posts) Making Microsoft 365 APIs faster and cheaper: batching, caching, ETags, change detection, and what a wasted API call actually costs you. Topic page: https://jeppe-spanggaard.dk/tags/api-performance/ Broad topic: these posts are listed under the narrower topics below. ### Azure Functions for Microsoft 365 (7 posts) Azure Functions as the backend for Microsoft 365 work: proxies that hide tokens, blob caching with 304s, Event Grid handlers and long-running provisioning. Topic page: https://jeppe-spanggaard.dk/tags/azure-functions/ - [Stop Re-Downloading Unchanged Blobs: Let Azure Answer 304 for You](https://jeppe-spanggaard.dk/blogs/azure-blob-storage-etag-304/) (2026-07-05): Learn how to forward the browser's If-None-Match header to Azure Blob Storage with BlobRequestConditions, so unchanged files never leave storage at all. - [One Endpoint, Whole Website: Serving a Static Site Through an Azure Function](https://jeppe-spanggaard.dk/blogs/azure-function-blob-storage-static-site-proxy/) (2026-06-25): Learn how a single Azure Function with a catch-all route can proxy an entire static website out of a private Blob Storage container, with auth in front of every file. ### Durable Functions (3 posts) Durable Functions in practice: checkpointing, idempotent activities, deterministic instance IDs, and telling a permanent failure from a transient one. Topic page: https://jeppe-spanggaard.dk/tags/durable-functions/ Start here: Durable Functions: A Function That Sleeps for a Week - https://jeppe-spanggaard.dk/blogs/what-are-durable-functions/ - [Every Activity Will Run Twice: Idempotency in Durable Functions](https://jeppe-spanggaard.dk/blogs/durable-functions-idempotent-activities/) (2026-08-05): Learn how to make every Durable Functions activity idempotent so retry policies can safely rerun SharePoint and Graph provisioning steps. - [Provision Forward, Never Backward: Checkpointing Durable Functions](https://jeppe-spanggaard.dk/blogs/durable-functions-provisioning-checkpoints/) (2026-07-16): Learn how to checkpoint a Durable Functions orchestration so a failed SharePoint provisioning run resumes where it stopped instead of starting over. - [Durable Functions: A Function That Sleeps for a Week](https://jeppe-spanggaard.dk/blogs/what-are-durable-functions/) (2026-07-15): Learn what Azure Durable Functions are and what they can do, using a SharePoint site provisioning workflow as a real-world example. ### Outlook Add-ins on Mobile (4 posts) Outlook add-ins that survive mobile: requirement sets that lie about their version, Nested App Auth without a popup, and falling back to Microsoft Graph. Topic page: https://jeppe-spanggaard.dk/tags/outlook-add-ins/ - [Your Add-in Gets a Free Folder in Everyone's OneDrive (Use It)](https://jeppe-spanggaard.dk/blogs/outlook-addin-onedrive-approot-preferences/) (2026-06-15): Learn how to store Outlook add-in user preferences in the OneDrive App Folder via the Graph approot endpoint, so settings roam across every device. - [Same Email, Different Source: Falling Back to Microsoft Graph on Outlook Mobile](https://jeppe-spanggaard.dk/blogs/outlook-addin-graph-fallback-mobile/) (2026-06-05): Learn how to fall back to the Microsoft Graph MIME endpoint when getAsFileAsync isn't available in Outlook on mobile, so your add-in keeps working everywhere. - [There's No Popup on a Phone: Nested App Auth for Outlook Add-ins](https://jeppe-spanggaard.dk/blogs/outlook-addin-nested-app-auth-mobile/) (2026-05-28): Learn how to get MSAL tokens inside Outlook on mobile with Nested App Auth (NAA), from the brk-multihub redirect URI to acquireTokenSilent with a loginHint. - [Why My Outlook Add-in Lies About Its Version (And Yours Should Too)](https://jeppe-spanggaard.dk/blogs/outlook-addin-manifest-requirement-sets-mobile/) (2026-05-12): Learn why declaring a low Mailbox requirement set in your Outlook add-in manifest is the key to mobile support, and how to feature-detect newer APIs at runtime. ### Authentication and Authorization (5 posts) Getting a token and keeping it safe: MSAL and Nested App Auth, PnP.Framework and Graph SDK auth, Easy Auth, and keeping third-party keys out of the browser. Topic page: https://jeppe-spanggaard.dk/tags/authentication/ - [F12 Shows Your API Key: Hiding Third-Party Tokens Behind an Azure Function](https://jeppe-spanggaard.dk/blogs/spfx-azure-function-api-proxy-hide-token/) (2026-07-25): Learn how to keep third-party API keys out of the browser by proxying SPFx web part calls through an Azure Function that swaps in the secret server-side. - [Microsoft Graph SDK Authentication in C#: Quick Start Guide](https://jeppe-spanggaard.dk/blogs/graph-sdk-authentication-csharp/) (2025-06-20): Quick setup guide for Microsoft Graph SDK using your existing app registration - [SPFx Audience targeting](https://jeppe-spanggaard.dk/blogs/role-based-rendering/) (2024-04-09): How to easy implement audience targeting for for one's components ### SharePoint Provisioning (4 posts) Provisioning SharePoint sites that survive contact with reality: PnP templates, eventual consistency, navigation, content types and Durable orchestrations. Topic page: https://jeppe-spanggaard.dk/tags/provisioning/ - [Stop Spamming Your Users: Create Microsoft 365 Groups Without Welcome Emails](https://jeppe-spanggaard.dk/blogs/graph-sdk-create-group-without-welcome-email/) (2026-02-14): Learn how to create Microsoft 365 groups programmatically with the Graph SDK in C# without sending a welcome email flood to every member. - [Update list to use content type](https://jeppe-spanggaard.dk/blogs/update-list-to-use-content-type/) (2024-11-10): We've all tried creating a SharePoint list without using Content Types from the start. It seems like the quick solution, but it can lead to challenges later when restructuring the list. This post explains why using Content Types from the beginning is a smart move that can save time and hassle in the long run. ### PnP Framework and PnP Core (4 posts) Working with the PnP libraries: template sizing and resilience, authentication, nested navigation, and what is still missing in PnP.Core. Topic page: https://jeppe-spanggaard.dk/tags/pnp/ - [PnP Core vs PnP.Framework: Why I Haven't Switched Yet](https://jeppe-spanggaard.dk/blogs/pnp-core-vs-pnp-framework-migration-blockers/) (2026-07-27): Learn why I'm still on PnP.Framework instead of PnP Core, the two blockers holding the migration back, and what has to land before I make the jump. - [PnP.Framework Authentication in C#: A Beginner's Guide](https://jeppe-spanggaard.dk/blogs/pnp-framework-authentication-csharp/) (2025-06-03): Learn to authenticate C# applications with PnP.Framework and Azure using certificates in this beginner's guide to app registration and permissions. ### Graph Webhooks and Notifications (1 post) Change notifications from Microsoft 365: rich notifications that carry their own metadata, validating and decrypting payloads, and debouncing users who cannot stop clicking save. Topic page: https://jeppe-spanggaard.dk/tags/webhooks/ - [Optimize SharePoint Webhooks: Debouncing User Actions](https://jeppe-spanggaard.dk/blogs/optimize-sharepoint-webhooks-debouncing-user-actions/) (2025-10-14): Learn how to effectively debounce SharePoint webhooks to optimize performance and reduce unnecessary operations when users frequently click save. ### Throttling and Rate Limits (3 posts) What happens when Microsoft 365 says 429: smart retry logic that is not just retrying everything, Retry-After, and testing your limits before production does. Topic page: https://jeppe-spanggaard.dk/tags/throttling/ - [SharePoint CSOM: Prevent Unnecessary Updates and API Calls](https://jeppe-spanggaard.dk/blogs/sharepoint-csom-prevent-unnecessary-updates/) (2025-11-05): The SharePoint API call that wasted resources is now optimized with smart change detection, reducing unnecessary updates and costs. - [Stop Retrying Everything: Smart Graph Batch Retry Logic](https://jeppe-spanggaard.dk/blogs/graph-batch-smart-retry/) (2025-09-22): Learn smart retry logic for Microsoft Graph batching to optimize API calls, reduce throttling, and enhance user experience. ### Dev Proxy Recipes (2 posts) Using Dev Proxy to break Microsoft 365 APIs on purpose: simulating throttling and failures locally so you find out before your users do. Topic page: https://jeppe-spanggaard.dk/tags/dev-proxy/ - [One Big PnP Template or Many Small Ones?](https://jeppe-spanggaard.dk/blogs/pnp-template-sizing-resilience/) (2026-04-25): After testing with DevProxy, I found that monolithic PnP templates silently destroy your retry strategy. Here's what I learned. - [DevProxy: How to Test API Rate Limiting and Throttling in C# Development](https://jeppe-spanggaard.dk/blogs/devproxy-throttling-testing/) (2025-08-10): Discover how to simulate Microsoft Graph and SharePoint throttling locally using DevProxy, and prevent production slowdowns before they happen. ### C# Reflection and Attributes (3 posts) Reflection and custom attributes in C#: logging without boilerplate, mapping entities to lists, and generating audit trails from type metadata. Topic page: https://jeppe-spanggaard.dk/tags/reflection/ - [Web Api logging attribute](https://jeppe-spanggaard.dk/blogs/logging-before-execution-w-attributes/) (2024-02-18): Implement logging only with a attribute on each method in a controller - [SQL save changes w. trigger](https://jeppe-spanggaard.dk/blogs/trigger-in-mssql-for-version-history/) (2024-02-03): Easiest way to keep track of which changes have been made to an object, and likewise, who has carried out these changes. - [EF for SharePoint](https://jeppe-spanggaard.dk/blogs/entity-framework-for-sharepoint/) (2023-07-23): ## All blog posts, newest first - [Every Activity Will Run Twice: Idempotency in Durable Functions](https://jeppe-spanggaard.dk/blogs/durable-functions-idempotent-activities/) (2026-08-05): Learn how to make every Durable Functions activity idempotent so retry policies can safely rerun SharePoint and Graph provisioning steps. - [CSOM vs SharePoint REST vs Graph: My Pick-One Playbook](https://jeppe-spanggaard.dk/blogs/csom-vs-sharepoint-rest-vs-graph/) (2026-07-30): Learn when to use CSOM, SharePoint REST or Microsoft Graph for SharePoint work, and which operations each one silently refuses to do. - [PnP Core vs PnP.Framework: Why I Haven't Switched Yet](https://jeppe-spanggaard.dk/blogs/pnp-core-vs-pnp-framework-migration-blockers/) (2026-07-27): Learn why I'm still on PnP.Framework instead of PnP Core, the two blockers holding the migration back, and what has to land before I make the jump. - [F12 Shows Your API Key: Hiding Third-Party Tokens Behind an Azure Function](https://jeppe-spanggaard.dk/blogs/spfx-azure-function-api-proxy-hide-token/) (2026-07-25): Learn how to keep third-party API keys out of the browser by proxying SPFx web part calls through an Azure Function that swaps in the secret server-side. - [Provision Forward, Never Backward: Checkpointing Durable Functions](https://jeppe-spanggaard.dk/blogs/durable-functions-provisioning-checkpoints/) (2026-07-16): Learn how to checkpoint a Durable Functions orchestration so a failed SharePoint provisioning run resumes where it stopped instead of starting over. - [Durable Functions: A Function That Sleeps for a Week](https://jeppe-spanggaard.dk/blogs/what-are-durable-functions/) (2026-07-15): Learn what Azure Durable Functions are and what they can do, using a SharePoint site provisioning workflow as a real-world example. - [Stop Re-Downloading Unchanged Blobs: Let Azure Answer 304 for You](https://jeppe-spanggaard.dk/blogs/azure-blob-storage-etag-304/) (2026-07-05): Learn how to forward the browser's If-None-Match header to Azure Blob Storage with BlobRequestConditions, so unchanged files never leave storage at all. - [One Endpoint, Whole Website: Serving a Static Site Through an Azure Function](https://jeppe-spanggaard.dk/blogs/azure-function-blob-storage-static-site-proxy/) (2026-06-25): Learn how a single Azure Function with a catch-all route can proxy an entire static website out of a private Blob Storage container, with auth in front of every file. - [Your Add-in Gets a Free Folder in Everyone's OneDrive (Use It)](https://jeppe-spanggaard.dk/blogs/outlook-addin-onedrive-approot-preferences/) (2026-06-15): Learn how to store Outlook add-in user preferences in the OneDrive App Folder via the Graph approot endpoint, so settings roam across every device. - [Same Email, Different Source: Falling Back to Microsoft Graph on Outlook Mobile](https://jeppe-spanggaard.dk/blogs/outlook-addin-graph-fallback-mobile/) (2026-06-05): Learn how to fall back to the Microsoft Graph MIME endpoint when getAsFileAsync isn't available in Outlook on mobile, so your add-in keeps working everywhere. - [There's No Popup on a Phone: Nested App Auth for Outlook Add-ins](https://jeppe-spanggaard.dk/blogs/outlook-addin-nested-app-auth-mobile/) (2026-05-28): Learn how to get MSAL tokens inside Outlook on mobile with Nested App Auth (NAA), from the brk-multihub redirect URI to acquireTokenSilent with a loginHint. - [The SharePoint CSOM Performance Playbook: Stop Paying for Wasted API Calls](https://jeppe-spanggaard.dk/blogs/sharepoint-csom-performance-playbook/) (2026-05-20): Learn how to speed up SharePoint CSOM with five proven techniques - batching, CAML joins, change detection, server-side exception handling, and fast taxonomy loading. - [Why My Outlook Add-in Lies About Its Version (And Yours Should Too)](https://jeppe-spanggaard.dk/blogs/outlook-addin-manifest-requirement-sets-mobile/) (2026-05-12): Learn why declaring a low Mailbox requirement set in your Outlook add-in manifest is the key to mobile support, and how to feature-detect newer APIs at runtime. - [One Big PnP Template or Many Small Ones?](https://jeppe-spanggaard.dk/blogs/pnp-template-sizing-resilience/) (2026-04-25): After testing with DevProxy, I found that monolithic PnP templates silently destroy your retry strategy. Here's what I learned. - [SharePoint News Links via Graph SDK: Filling in the Gaps the Docs Left Behind](https://jeppe-spanggaard.dk/blogs/graph-beta-sdk-news-link-with-banner-image/) (2026-03-03): Learn how to create SharePoint News Link pages with a banner image using the Microsoft Graph Beta SDK in C# — including the multipart upload the official documentation leaves completely undocumented. - [Stop Spamming Your Users: Create Microsoft 365 Groups Without Welcome Emails](https://jeppe-spanggaard.dk/blogs/graph-sdk-create-group-without-welcome-email/) (2026-02-14): Learn how to create Microsoft 365 groups programmatically with the Graph SDK in C# without sending a welcome email flood to every member. - [CSOM Performance: Fast Taxonomy Loading for SharePoint List Items with Many Terms](https://jeppe-spanggaard.dk/blogs/csom-taxonomy-performance-optimization/) (2025-12-21) [updated 2026-07-26]: An undocumented but effective approach to dramatically speed up CSOM taxonomy field loading when dealing with SharePoint list items that have many terms. - [SharePoint CSOM: Prevent Unnecessary Updates and API Calls](https://jeppe-spanggaard.dk/blogs/sharepoint-csom-prevent-unnecessary-updates/) (2025-11-05) [updated 2026-07-26]: The SharePoint API call that wasted resources is now optimized with smart change detection, reducing unnecessary updates and costs. - [SharePoint's Server-Side Try-Catch: ExceptionHandlingScope](https://jeppe-spanggaard.dk/blogs/sharepoint-exceptionhandlingscope-csom-ensureuser/) (2025-10-20) [updated 2026-07-26]: Discover how SharePoint's ExceptionHandlingScope can optimize your API calls, reduce costs, and enhance performance with server-side exception handling. - [Optimize SharePoint Webhooks: Debouncing User Actions](https://jeppe-spanggaard.dk/blogs/optimize-sharepoint-webhooks-debouncing-user-actions/) (2025-10-14): Learn how to effectively debounce SharePoint webhooks to optimize performance and reduce unnecessary operations when users frequently click save. - [Stop Retrying Everything: Smart Graph Batch Retry Logic](https://jeppe-spanggaard.dk/blogs/graph-batch-smart-retry/) (2025-09-22): Learn smart retry logic for Microsoft Graph batching to optimize API calls, reduce throttling, and enhance user experience. - [Graph Batching for File Content: Mapping Requests to Responses](https://jeppe-spanggaard.dk/blogs/graph-batching-file-content-mapping/) (2025-09-10): How to handle Graph batching when downloading file content and mapping responses back to original requests - [CSOM Performance Optimization: Why You Should Batch Your SharePoint Operations](https://jeppe-spanggaard.dk/blogs/csom-performance-optimization-chunking/) (2025-08-31) [updated 2026-07-26]: Learn how to dramatically improve CSOM performance by batching operations instead of executing them one by one - [DevProxy: How to Test API Rate Limiting and Throttling in C# Development](https://jeppe-spanggaard.dk/blogs/devproxy-throttling-testing/) (2025-08-10): Discover how to simulate Microsoft Graph and SharePoint throttling locally using DevProxy, and prevent production slowdowns before they happen. - [Efficient Multi-List Queries in CSOM: Using CAML Joins with CAMLEX](https://jeppe-spanggaard.dk/blogs/joining-multiple-lists-csom-caml/) (2025-07-28) [updated 2026-07-26]: Learn how to efficiently query multiple SharePoint lists using CAML joins instead of multiple API calls to avoid throttling - [Microsoft Graph SDK Authentication in C#: Quick Start Guide](https://jeppe-spanggaard.dk/blogs/graph-sdk-authentication-csharp/) (2025-06-20): Quick setup guide for Microsoft Graph SDK using your existing app registration - [PnP.Framework Authentication in C#: A Beginner's Guide](https://jeppe-spanggaard.dk/blogs/pnp-framework-authentication-csharp/) (2025-06-03): Learn to authenticate C# applications with PnP.Framework and Azure using certificates in this beginner's guide to app registration and permissions. - [Export files as zip from SharePoint](https://jeppe-spanggaard.dk/blogs/download-multiple-files-from-sharepoint/) (2024-12-01): Download files from SharePoint as a zip, as is possible through the UI - [Update list to use content type](https://jeppe-spanggaard.dk/blogs/update-list-to-use-content-type/) (2024-11-10): We've all tried creating a SharePoint list without using Content Types from the start. It seems like the quick solution, but it can lead to challenges later when restructuring the list. This post explains why using Content Types from the beginning is a smart move that can save time and hassle in the long run. - [SPFx Audience targeting](https://jeppe-spanggaard.dk/blogs/role-based-rendering/) (2024-04-09): How to easy implement audience targeting for for one's components - [Web Api logging attribute](https://jeppe-spanggaard.dk/blogs/logging-before-execution-w-attributes/) (2024-02-18): Implement logging only with a attribute on each method in a controller - [SQL save changes w. trigger](https://jeppe-spanggaard.dk/blogs/trigger-in-mssql-for-version-history/) (2024-02-03): Easiest way to keep track of which changes have been made to an object, and likewise, who has carried out these changes. - [EF for SharePoint](https://jeppe-spanggaard.dk/blogs/entity-framework-for-sharepoint/) (2023-07-23):