{"id":185,"date":"2025-09-16T18:51:31","date_gmt":"2025-09-16T16:51:31","guid":{"rendered":"https:\/\/www.insync.co.za\/blog\/?p=185"},"modified":"2025-09-16T19:05:47","modified_gmt":"2025-09-16T17:05:47","slug":"11-asp-net-core-best-practices-with-examples","status":"publish","type":"post","link":"https:\/\/www.insync.co.za\/blog\/2025\/09\/16\/11-asp-net-core-best-practices-with-examples\/","title":{"rendered":"11 ASP.NET Core Best Practices (with Examples)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Learn 11 ASP.NET Core best practices with examples: async programming, memory management, HttpClientFactory, efficient middleware, and more to boost performance and scalability.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Introduction<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Building a web app with ASP.NET Core is more than just writing endpoints and views \u2014 the real test is how your app behaves under load: how quickly it responds, how many users it can serve, and how stable it stays when things go wrong.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Too often, developers assume <em>\u201cif it works, it\u2019s good enough.\u201d<\/em> In reality, small inefficiencies \u2014 blocking threads, unnecessary memory allocations, or unoptimized database queries \u2014 can snowball into slowdowns, high resource usage, or even crashes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The .NET runtime places objects of <strong>85,000 bytes or more<\/strong> into the <em>Large Object Heap (LOH)<\/em>. LOH cleanup only happens during full (generation 2) garbage collections, which are expensive and pause execution (<a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/standard\/garbage-collection\/large-object-heap?utm_source=chatgpt.com\">Microsoft Learn<\/a>).<\/li>\n\n\n\n<li>Blocking calls (<code>.Result<\/code>, <code>.Wait()<\/code>) in ASP.NET Core can cause <strong>thread pool starvation<\/strong>. Threads get tied up waiting, leaving fewer available for new requests. Microsoft explicitly warns against blocking calls in hot code paths (<a href=\"https:\/\/learn.microsoft.com\/en-us\/aspnet\/core\/fundamentals\/best-practices?view=aspnetcore-9.0&amp;utm_source=chatgpt.com\">Microsoft Learn<\/a>).<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The good news? These issues are avoidable. Below are <strong>11 best practices<\/strong>, with examples, to help you build apps that are faster, more scalable, and production-ready.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">1. Use Asynchronous APIs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">When you block on I\/O, you waste threads. Always use <code>async<\/code>\/<code>await<\/code> for database, HTTP, and file calls.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u274c <strong>Blocking example:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-d4fef9c5fa7b97ca92283d261501806d\"><code>public IActionResult GetData()\n{\n    var result = _service.GetDataAsync().Result; \/\/ Blocks thread\n    return Ok(result);\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705 <strong>Async example:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-c078ec770f394c767cd83d80296cd048\"><code>public async Task&lt;IActionResult&gt; GetData()\n{\n    var result = await _service.GetDataAsync();\n    return Ok(result);\n}\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">2. Avoid Returning Huge Lists<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Don\u2019t dump entire tables into memory. Use paging or streaming.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u274c <strong>Bad:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-1edded8545bf3323ac73b40b7876e522\"><code>public async Task&lt;IActionResult&gt; GetUsers()\n{\n    return Ok(await _db.Users.ToListAsync());\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705 <strong>Better (with paging):<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-b7d6f21bdcaa0dad92694ad40032f239\"><code>public async Task&lt;IActionResult&gt; GetUsers(int page = 1, int pageSize = 50)\n{\n    var users = await _db.Users\n                         .Skip((page - 1) * pageSize)\n                         .Take(pageSize)\n                         .ToListAsync();\n    return Ok(users);\n}\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">3. Reuse Large Objects<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Large arrays go into the LOH. Instead of recreating them every time, reuse with pooling.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705 <strong>Using ArrayPool:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-de67be11068abcfbfd5c60b0de4e8638\"><code>var pool = ArrayPool&lt;byte&gt;.Shared;\nbyte&#91;] buffer = pool.Rent(1024);\n\n\/\/ use buffer...\n\npool.Return(buffer);\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">4. Optimize Data Access<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Load only what you need.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u274c <strong>Bad:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-88c87382aa3791de29d7e8290adb29b8\"><code>var users = await _db.Users.ToListAsync(); \n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705 <strong>Better:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-6241e350a1636a8667840643ae921398\"><code>var usernames = await _db.Users\n                         .Select(u =&gt; u.Username)\n                         .ToListAsync();\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For read-only queries:<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-c9594075c15c9556dc3e2293371551f5\"><code>var users = await _db.Users\n                     .AsNoTracking()\n                     .ToListAsync();\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">5. Use <code>HttpClientFactory<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Creating new <code>HttpClient<\/code> instances exhausts sockets. Use <code>HttpClientFactory<\/code> instead.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705 <strong>Good:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-c82b52d4bb81fb1c13717bd340e704ac\"><code>public class MyService\n{\n    private readonly HttpClient _client;\n\n    public MyService(HttpClient client)\n    {\n        _client = client;\n    }\n\n    public Task&lt;string&gt; GetDataAsync() =&gt;\n        _client.GetStringAsync(\"https:\/\/api.example.com\/data\");\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Registered in <code>Program.cs<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-57ddc8ef3c01965695a124b09e85ce02\"><code>builder.Services.AddHttpClient&lt;MyService&gt;();\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">6. Keep Middleware Lean<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Every request runs through middleware, so keep it efficient.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705 <strong>Example:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-9a012b1594a6fd07675c13fd7d0b587b\"><code>app.Use(async (context, next) =&gt;\n{\n    var stopwatch = Stopwatch.StartNew();\n    await next();\n    stopwatch.Stop();\n    Console.WriteLine($\"Request took {stopwatch.ElapsedMilliseconds}ms\");\n});\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">7. Offload Long Tasks<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Don\u2019t block user requests with heavy tasks. Run them in the background.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705 <strong>BackgroundService example:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-8c43439f89d13a6a05b2d7d4e2d1e4ee\"><code>public class EmailQueueService : BackgroundService\n{\n    protected override async Task ExecuteAsync(CancellationToken stoppingToken)\n    {\n        while (!stoppingToken.IsCancellationRequested)\n        {\n            var email = await _queue.GetNextAsync();\n            await _emailSender.SendAsync(email);\n        }\n    }\n}\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">8. Minify &amp; Compress Responses<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Reduce payload size with compression.<\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-617ff1a6bbaab5c2acf918003d086da7\"><code>builder.Services.AddResponseCompression();\napp.UseResponseCompression();\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">9. Use the Latest Versions<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Each new .NET release comes with <strong>performance improvements<\/strong> (e.g., .NET 8 improved JSON serialization and async handling). Stay updated to benefit from these gains.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">10. Use Exceptions Correctly<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Exceptions should not be your logic flow.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u274c <strong>Bad:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-e85b7496b2ae6f6f076049f8399ec874\"><code>try\n{\n    var user = await _db.Users.FirstAsync(u =&gt; u.Id == id);\n}\ncatch\n{\n    return NotFound();\n}\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705 <strong>Better:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-7cbd35b0c52965025459fe5dc2f5ff6c\"><code>var user = await _db.Users.FirstOrDefaultAsync(u =&gt; u.Id == id);\nif (user == null) return NotFound();\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">11. Be Careful with <code>HttpContext<\/code><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Don\u2019t pass <code>HttpContext<\/code> across threads or store it for later use.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u274c <strong>Bad:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-c1c38ce130b528ab335c761254cfb2b1\"><code>Task.Run(() =&gt;\n{\n    var user = context.User.Identity.Name; \/\/ risky\n});\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705 <strong>Better:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code has-white-color has-black-background-color has-text-color has-background has-link-color wp-elements-c7b95238ba56c9a59272a7b682197d25\"><code>var user = context.User.Identity.Name;\n\/\/ Pass the value, not HttpContext itself\n<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Making your ASP.NET Core app production-ready isn\u2019t just about \u201cmaking it work\u201d \u2014 it\u2019s about making it <em>work well under real-world conditions<\/em>. The practices above \u2014 async programming, smart memory usage, efficient data access, lean middleware, and proper resource handling \u2014 are based on how the .NET runtime works and what typically breaks in production.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Key Takeaways:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Measure before optimizing<\/strong>: Use profiling tools (PerfView, Visual Studio diagnostics).<\/li>\n\n\n\n<li><strong>Optimize hot paths first<\/strong>: Middleware, logging, and authentication run on every request.<\/li>\n\n\n\n<li><strong>Balance performance and maintainability<\/strong>: Don\u2019t over-engineer unless the gains are real.<\/li>\n\n\n\n<li><strong>Stay updated<\/strong>: New .NET releases bring performance and security improvements.<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">If your business needs help applying these principles in a real-world project, our team at <a href=\"https:\/\/www.insync.co.za\">InSync Software<\/a> can help. We specialize in building tailored solutions on ASP.NET Core that are fast, scalable, and designed around your business goals.<\/p>\n\n\n\n\n\n<h2 class=\"wp-block-heading\">References &amp; Further Reading<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/code-maze.com\/asynchronous-programming-with-async-and-await-in-asp-net-core\/?utm_source=chatgpt.com\">Asynchronous Programming with Async and Await in ASP.NET Core (Code Maze)<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/learn.microsoft.com\/en-us\/aspnet\/core\/fundamentals\/best-practices?view=aspnetcore-9.0&amp;utm_source=chatgpt.com\">ASP.NET Core Best Practices \u2014 Microsoft Learn<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/learn.microsoft.com\/en-us\/dotnet\/standard\/garbage-collection\/large-object-heap?utm_source=chatgpt.com\">Large Object Heap (LOH) \u2014 .NET Documentation<\/a><\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udc49 <strong>Your turn:<\/strong> Which of these best practices do you already follow in your ASP.NET Core projects? Share your thoughts in the comments \u2014 I\u2019d love to hear how you optimize your apps.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Learn 11 ASP.NET Core best practices with examples: async programming, memory management, HttpClientFactory, efficient middleware, and more to boost performance and scalability. Introduction Building a web app with ASP.NET Core is more than just writing endpoints and views \u2014 the real test is how your app behaves under load: how quickly it responds, how many [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":188,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"pagelayer_contact_templates":[],"_pagelayer_content":"","footnotes":""},"categories":[18],"tags":[37,30,19],"class_list":["post-185","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software-development","tag-net","tag-c","tag-software"],"_links":{"self":[{"href":"https:\/\/www.insync.co.za\/blog\/wp-json\/wp\/v2\/posts\/185","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.insync.co.za\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.insync.co.za\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.insync.co.za\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.insync.co.za\/blog\/wp-json\/wp\/v2\/comments?post=185"}],"version-history":[{"count":3,"href":"https:\/\/www.insync.co.za\/blog\/wp-json\/wp\/v2\/posts\/185\/revisions"}],"predecessor-version":[{"id":192,"href":"https:\/\/www.insync.co.za\/blog\/wp-json\/wp\/v2\/posts\/185\/revisions\/192"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.insync.co.za\/blog\/wp-json\/wp\/v2\/media\/188"}],"wp:attachment":[{"href":"https:\/\/www.insync.co.za\/blog\/wp-json\/wp\/v2\/media?parent=185"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.insync.co.za\/blog\/wp-json\/wp\/v2\/categories?post=185"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.insync.co.za\/blog\/wp-json\/wp\/v2\/tags?post=185"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}